From b6d9d16251ade0eed3466564d99e57645d30f9cb Mon Sep 17 00:00:00 2001 From: Henry Lee Date: Thu, 30 Nov 2023 20:25:36 +0900 Subject: [PATCH 1/2] refactor(java): maven-shared-utils --- Java-base/maven-shared-utils/Dockerfile | 28 + Java-base/maven-shared-utils/src/Jenkinsfile | 20 + Java-base/maven-shared-utils/src/README.md | 99 + .../maven-shared-utils/src/deploySite.sh | 23 + .../src/findbugs-exclude.xml | 83 + Java-base/maven-shared-utils/src/pom.xml | 164 + .../org/apache/maven/shared/utils/Os.java | 430 +++ .../apache/maven/shared/utils/PathTool.java | 355 ++ .../maven/shared/utils/PropertyUtils.java | 218 ++ .../maven/shared/utils/ReaderFactory.java | 213 ++ .../maven/shared/utils/StringUtils.java | 2551 ++++++++++++++ .../maven/shared/utils/WriterFactory.java | 194 ++ .../utils/cli/AbstractStreamHandler.java | 62 + .../apache/maven/shared/utils/cli/Arg.java | 52 + .../shared/utils/cli/CommandLineCallable.java | 37 + .../utils/cli/CommandLineException.java | 49 + .../cli/CommandLineTimeOutException.java | 44 + .../shared/utils/cli/CommandLineUtils.java | 628 ++++ .../maven/shared/utils/cli/Commandline.java | 528 +++ .../shared/utils/cli/DefaultConsumer.java | 44 + .../shared/utils/cli/ShutdownHookUtils.java | 73 + .../shared/utils/cli/StreamConsumer.java | 43 + .../maven/shared/utils/cli/StreamFeeder.java | 151 + .../maven/shared/utils/cli/StreamPumper.java | 161 + .../utils/cli/WriterStreamConsumer.java | 56 + .../utils/cli/javatool/AbstractJavaTool.java | 360 ++ .../cli/javatool/AbstractJavaToolRequest.java | 75 + .../shared/utils/cli/javatool/JavaTool.java | 72 + .../utils/cli/javatool/JavaToolException.java | 55 + .../utils/cli/javatool/JavaToolRequest.java | 66 + .../utils/cli/javatool/JavaToolResult.java | 110 + .../shared/utils/cli/shell/BourneShell.java | 138 + .../shared/utils/cli/shell/CmdShell.java | 97 + .../shared/utils/cli/shell/CommandShell.java | 42 + .../maven/shared/utils/cli/shell/Shell.java | 418 +++ .../shared/utils/introspection/ClassMap.java | 538 +++ .../introspection/IntrospectionException.java | 41 + .../shared/utils/introspection/MethodMap.java | 479 +++ .../ReflectionValueExtractor.java | 401 +++ .../shared/utils/io/DirectoryScanResult.java | 62 + .../shared/utils/io/DirectoryScanner.java | 932 +++++ .../utils/io/DirectoryWalkListener.java | 56 + .../maven/shared/utils/io/FileUtils.java | 2146 ++++++++++++ .../apache/maven/shared/utils/io/IOUtil.java | 1368 ++++++++ .../maven/shared/utils/io/Java7Support.java | 106 + .../maven/shared/utils/io/MatchPattern.java | 155 + .../maven/shared/utils/io/MatchPatterns.java | 96 + .../maven/shared/utils/io/ScanConductor.java | 90 + .../maven/shared/utils/io/SelectorUtils.java | 819 +++++ .../maven/shared/utils/io/WalkCollector.java | 83 + .../utils/logging/AnsiMessageBuilder.java | 156 + .../utils/logging/LoggerLevelRenderer.java | 53 + .../shared/utils/logging/MessageBuilder.java | 103 + .../shared/utils/logging/MessageUtils.java | 204 ++ .../utils/logging/PlainMessageBuilder.java | 142 + .../maven/shared/utils/logging/Style.java | 182 + .../shared/utils/logging/package-info.java | 51 + .../utils/xml/PrettyPrintXMLWriter.java | 393 +++ .../maven/shared/utils/xml/XMLEncode.java | 378 ++ .../maven/shared/utils/xml/XMLWriter.java | 90 + .../shared/utils/xml/XmlReaderException.java | 167 + .../shared/utils/xml/XmlStreamReader.java | 174 + .../utils/xml/XmlStreamReaderException.java | 81 + .../shared/utils/xml/XmlStreamWriter.java | 50 + .../maven/shared/utils/xml/XmlWriterUtil.java | 362 ++ .../maven/shared/utils/xml/Xpp3Dom.java | 429 +++ .../shared/utils/xml/Xpp3DomBuilder.java | 295 ++ .../maven/shared/utils/xml/Xpp3DomUtils.java | 162 + .../maven/shared/utils/xml/Xpp3DomWriter.java | 96 + .../xml/pull/XmlPullParserException.java | 61 + .../src/src/main/resources/META-INF/NOTICE | 2 + .../apache/maven/shared/utils/annotations.xml | 229 ++ .../src/src/site/apt/index.apt.vm | 52 + .../src/src/site/resources/download.cgi | 22 + .../maven-shared-utils/src/src/site/site.xml | 38 + .../src/src/site/xdoc/download.xml.vm | 126 + .../apache/maven/shared/utils/CaseTest.java | 172 + .../org/apache/maven/shared/utils/OsTest.java | 189 + .../maven/shared/utils/PathToolTest.java | 158 + .../maven/shared/utils/PropertyUtilsTest.java | 195 ++ .../maven/shared/utils/StringUtilsTest.java | 2080 +++++++++++ .../shared/utils/XmlStreamReaderTest.java | 208 ++ .../utils/cli/CommandLineUtilsTest.java | 171 + .../utils/cli/shell/BourneShellTest.java | 206 ++ .../utils/exceptionutils/TestException.java | 56 + .../TestExceptionWithDetail.java | 46 + .../ReflectionValueExtractorTest.java | 525 +++ .../shared/utils/io/DirectoryScannerTest.java | 452 +++ .../maven/shared/utils/io/FileUtilsTest.java | 1785 ++++++++++ .../maven/shared/utils/io/IOUtilTest.java | 3043 +++++++++++++++++ .../shared/utils/io/MatchPatternTest.java | 38 + .../shared/utils/io/MatchPatternsTest.java | 40 + .../shared/utils/io/SelectorUtilsTest.java | 84 + .../shared/utils/io/SymlinkTestSetup.java | 62 + .../utils/logging/AnsiMessageBuilderTest.java | 111 + .../utils/logging/MessageUtilsTest.java | 48 + .../utils/testhelpers/FileTestHelper.java | 104 + .../utils/xml/PrettyPrintXmlWriterTest.java | 192 ++ .../shared/utils/xml/XmlWriterUtilTest.java | 436 +++ .../shared/utils/xml/Xpp3DomBuilderTest.java | 185 + .../shared/utils/xml/pull/Xpp3DomTest.java | 273 ++ .../directorywalker/directory1/file1.txt | 0 .../directory2/directory21/file21.txt | 0 .../directorywalker/directory2/file2.txt | 0 .../test/resources/directorywalker/file.txt | 0 .../src/src/test/resources/executable | 1 + .../src/test/resources/expand/expand_test.zip | Bin 0 -> 364 bytes .../dirOnTheOutside/FileInDirOnTheOutside.txt | 19 + .../test/resources/symlinks/onTheOutside.txt | 20 + .../src/src/test/resources/symlinks/regen.sh | 23 + .../symlinks/src/aRegularDir/aRegularFile.txt | 19 + .../src/test/resources/symlinks/src/fileR.txt | 20 + .../src/test/resources/symlinks/src/fileW.txt | 19 + .../src/test/resources/symlinks/src/fileX.txt | 19 + .../symlinks/src/symDir/targetFile.txt | 19 + .../FileInDirOnTheOutside.txt | 19 + .../symlinks/src/symLinkToFileOnTheOutside | 20 + .../src/src/test/resources/symlinks/src/symR | 20 + .../src/src/test/resources/symlinks/src/symW | 19 + .../src/src/test/resources/symlinks/src/symX | 19 + .../symlinks/src/targetDir/targetFile.txt | 19 + .../src/test/resources/symlinks/symlinks.tar | Bin 0 -> 13824 bytes .../src/test/resources/symlinks/symlinks.zip | Bin 0 -> 1616 bytes 123 files changed, 30343 insertions(+) create mode 100644 Java-base/maven-shared-utils/Dockerfile create mode 100644 Java-base/maven-shared-utils/src/Jenkinsfile create mode 100644 Java-base/maven-shared-utils/src/README.md create mode 100755 Java-base/maven-shared-utils/src/deploySite.sh create mode 100755 Java-base/maven-shared-utils/src/findbugs-exclude.xml create mode 100644 Java-base/maven-shared-utils/src/pom.xml create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/Os.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/PathTool.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/PropertyUtils.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/ReaderFactory.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/StringUtils.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/WriterFactory.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/AbstractStreamHandler.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/Arg.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineCallable.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineException.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineTimeOutException.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/DefaultConsumer.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/ShutdownHookUtils.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/WriterStreamConsumer.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/AbstractJavaTool.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/AbstractJavaToolRequest.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaTool.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolException.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolRequest.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolResult.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/CommandShell.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/ClassMap.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/IntrospectionException.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/MethodMap.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanResult.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryWalkListener.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/FileUtils.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/IOUtil.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/Java7Support.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/ScanConductor.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/WalkCollector.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/AnsiMessageBuilder.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/LoggerLevelRenderer.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/MessageBuilder.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/MessageUtils.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/PlainMessageBuilder.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/Style.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/package-info.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XMLWriter.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlReaderException.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamReader.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamReaderException.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamWriter.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlWriterUtil.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomBuilder.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomUtils.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomWriter.java create mode 100644 Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/pull/XmlPullParserException.java create mode 100644 Java-base/maven-shared-utils/src/src/main/resources/META-INF/NOTICE create mode 100644 Java-base/maven-shared-utils/src/src/main/resources/org/apache/maven/shared/utils/annotations.xml create mode 100644 Java-base/maven-shared-utils/src/src/site/apt/index.apt.vm create mode 100644 Java-base/maven-shared-utils/src/src/site/resources/download.cgi create mode 100644 Java-base/maven-shared-utils/src/src/site/site.xml create mode 100644 Java-base/maven-shared-utils/src/src/site/xdoc/download.xml.vm create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/CaseTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/OsTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/PathToolTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/PropertyUtilsTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/StringUtilsTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/XmlStreamReaderTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/cli/shell/BourneShellTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/exceptionutils/TestException.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/exceptionutils/TestExceptionWithDetail.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/DirectoryScannerTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/FileUtilsTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/IOUtilTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/SelectorUtilsTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/SymlinkTestSetup.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/logging/AnsiMessageBuilderTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/logging/MessageUtilsTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/testhelpers/FileTestHelper.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/PrettyPrintXmlWriterTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/XmlWriterUtilTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/Xpp3DomBuilderTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/pull/Xpp3DomTest.java create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory1/file1.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory2/directory21/file21.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory2/file2.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/directorywalker/file.txt create mode 100755 Java-base/maven-shared-utils/src/src/test/resources/executable create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/expand/expand_test.zip create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/dirOnTheOutside/FileInDirOnTheOutside.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/onTheOutside.txt create mode 100755 Java-base/maven-shared-utils/src/src/test/resources/symlinks/regen.sh create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/aRegularDir/aRegularFile.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileR.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileW.txt create mode 100755 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileX.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symDir/targetFile.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symLinkToDirOnTheOutside/FileInDirOnTheOutside.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symLinkToFileOnTheOutside create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symR create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symW create mode 100755 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symX create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/targetDir/targetFile.txt create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/symlinks.tar create mode 100644 Java-base/maven-shared-utils/src/src/test/resources/symlinks/symlinks.zip diff --git a/Java-base/maven-shared-utils/Dockerfile b/Java-base/maven-shared-utils/Dockerfile new file mode 100644 index 000000000..e208c4890 --- /dev/null +++ b/Java-base/maven-shared-utils/Dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:22.04 + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y software-properties-common \ + && add-apt-repository ppa:deadsnakes/ppa \ + && apt-get update \ + && apt-get install -y \ + build-essential \ + git \ + vim \ + jq \ + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/list/* + +RUN apt-get -y install sudo \ + openjdk-8-jdk \ + maven + +RUN bash -c "echo 2 | update-alternatives --config java" + +COPY src /workspace +WORKDIR /workspace + +RUN mvn install -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false + +RUN mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 + +ENV TZ=Asia/Seoul diff --git a/Java-base/maven-shared-utils/src/Jenkinsfile b/Java-base/maven-shared-utils/src/Jenkinsfile new file mode 100644 index 000000000..09ac70f12 --- /dev/null +++ b/Java-base/maven-shared-utils/src/Jenkinsfile @@ -0,0 +1,20 @@ +/** + * 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. + */ + +asfMavenTlpStdBuild() diff --git a/Java-base/maven-shared-utils/src/README.md b/Java-base/maven-shared-utils/src/README.md new file mode 100644 index 000000000..6ddc421da --- /dev/null +++ b/Java-base/maven-shared-utils/src/README.md @@ -0,0 +1,99 @@ + +Contributing to [Apache Maven Shared Utils](https://maven.apache.org/shared/maven-shared-utils/) +====================== + +[![ASF Jira](https://img.shields.io/endpoint?url=https%3A%2F%2Fmaven.apache.org%2Fbadges%2Fasf_jira-MSHARED.json)][jira] +[![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/apache/maven.svg?label=License)][license] +[![Maven Central](https://img.shields.io/maven-central/v/org.apache.maven.shared/maven-shared-utils.svg?label=Maven%20Central)](https://search.maven.org/artifact/org.apache.maven.shared/maven-shared-utils) +[![Jenkins Status](https://img.shields.io/jenkins/s/https/builds.apache.org/job/maven-box/job/maven-shared-utils/job/master.svg)][build] +[![Jenkins tests](https://img.shields.io/jenkins/t/https/builds.apache.org/job/maven-box/job/maven-shared-utils/job/master.svg)][test-results] + + +You have found a bug or you have an idea for a cool new feature? Contributing +code is a great way to give something back to the open source community. Before +you dig right into the code, there are a few guidelines that we need +contributors to follow so that we can have a chance of keeping on top of +things. + +Getting Started +--------------- + ++ Make sure you have a [JIRA account](https://issues.apache.org/jira/). ++ Make sure you have a [GitHub account](https://github.com/signup/free). ++ If you're planning to implement a new feature, it makes sense to discuss your changes + on the [dev list][ml-list] first. + This way you can make sure you're not wasting your time on something that isn't + considered to be in Apache Maven's scope. ++ Submit a ticket for your issue, assuming one does not already exist. + + Clearly describe the issue, including steps to reproduce when it is a bug. + + Make sure you fill in the earliest version that you know has the issue. ++ Fork the repository on GitHub. + +Making and Submitting Changes +-------------- + +We accept Pull Requests via GitHub. The [developer mailing list][ml-list] is the +main channel of communication for contributors. +There are some guidelines which will make applying PRs easier for us: ++ Create a topic branch from where you want to base your work (this is usually the master branch). + Push your changes to a topic branch in your fork of the repository. ++ Make commits of logical units. ++ Respect the original code style: by using the same [codestyle][code-style], + patches should only highlight the actual difference, not being disturbed by any formatting issues: + + Only use spaces for indentation. + + Create minimal diffs - disable on save actions like reformat source code or organize imports. + If you feel the source code should be reformatted, create a separate PR for this change. + + Check for unnecessary whitespace with `git diff --check` before committing. ++ Make sure your commit messages are in the proper format. Your commit message should contain the key of the JIRA issue. +``` +[MSHARED-XXX] - Subject of the JIRA Ticket + Optional supplemental description. +``` ++ Make sure you have added the necessary tests (JUnit/IT) for your changes. ++ Run all the tests with `mvn -Prun-its verify` to assure nothing else was accidentally broken. ++ Submit a pull request to the repository in the Apache organization. ++ Update your JIRA ticket and include a link to the pull request in the ticket. + +If you plan to contribute on a regular basis, please consider filing a [contributor license agreement][cla]. + +Making Trivial Changes +---------------------- + +For changes of a trivial nature to comments and documentation, it is not always +necessary to create a new ticket in JIRA. In this case, it is appropriate to +start the first line of a commit with '(doc)' instead of a ticket number. + +Additional Resources +-------------------- + ++ [Contributing patches](https://maven.apache.org/guides/development/guide-maven-development.html#Creating_and_submitting_a_patch) ++ [Apache Maven Shared Components project page][jira] ++ [Contributor License Agreement][cla] ++ [General GitHub documentation](https://help.github.com/) ++ [GitHub pull request documentation](https://help.github.com/send-pull-requests/) ++ [Apache Maven Twitter Account](https://twitter.com/ASFMavenProject) ++ #Maven IRC channel on freenode.org + +[jira]: https://issues.apache.org/jira/projects/MSHARED/ +[license]: https://www.apache.org/licenses/LICENSE-2.0 +[ml-list]: https://maven.apache.org/mailing-lists.html +[code-style]: https://maven.apache.org/developers/conventions/code.html +[cla]: https://www.apache.org/licenses/#clas +[maven-wiki]: https://cwiki.apache.org/confluence/display/MAVEN/Index +[test-results]: https://builds.apache.org/job/maven-box/job/maven-shared-utils/job/master/lastCompletedBuild/testReport/ +[build]: https://builds.apache.org/job/maven-box/job/maven-shared-utils/job/master/ diff --git a/Java-base/maven-shared-utils/src/deploySite.sh b/Java-base/maven-shared-utils/src/deploySite.sh new file mode 100755 index 000000000..f6c265d75 --- /dev/null +++ b/Java-base/maven-shared-utils/src/deploySite.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# +# 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. +# + +mvn -Preporting site site:stage $@ +mvn scm-publish:publish-scm $@ diff --git a/Java-base/maven-shared-utils/src/findbugs-exclude.xml b/Java-base/maven-shared-utils/src/findbugs-exclude.xml new file mode 100755 index 000000000..0fc3f2611 --- /dev/null +++ b/Java-base/maven-shared-utils/src/findbugs-exclude.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-shared-utils/src/pom.xml b/Java-base/maven-shared-utils/src/pom.xml new file mode 100644 index 000000000..714a52000 --- /dev/null +++ b/Java-base/maven-shared-utils/src/pom.xml @@ -0,0 +1,164 @@ + + + + 4.0.0 + + + org.apache.maven.shared + maven-shared-components + 34 + + + maven-shared-utils + 3.3.0-SNAPSHOT + + Apache Maven Shared Utils + Shared utilities for use by Maven core and plugins + + + scm:git:https://gitbox.apache.org/repos/asf/maven-shared-utils.git + scm:git:https://gitbox.apache.org/repos/asf/maven-shared-utils.git + https://github.com/apache/maven-shared-utils/tree/${project.scm.tag} + HEAD + + + jira + https://issues.apache.org/jira/issues/?jql=project%20%3D%20MSHARED%20AND%20component%20%3D%20maven-shared-utils + + + Jenkins + https://builds.apache.org/job/maven-box/job/maven-shared-utils/ + + + + apache.website + scm:svn:https://svn.apache.org/repos/asf/maven/website/components/${maven.site.path} + + + + + + Kathryn Newbould + + + + + RedundantThrows,NewlineAtEndOfFile,ParameterNumber,MethodLength,FileLength,ModifierOrder + + 7 + 3.1.0 + + + + + org.fusesource.jansi + jansi + 1.13 + true + + + junit + junit + 4.13 + test + + + org.hamcrest + hamcrest-core + 2.2 + test + + + commons-io + commons-io + 2.6 + + + org.apache.commons + commons-lang3 + 3.8.1 + test + + + org.apache.commons + commons-text + 1.3 + test + + + com.google.code.findbugs + jsr305 + 3.0.2 + provided + + + + org.apache.maven + maven-core + ${mavenVersion} + test + + + org.codehaus.plexus + plexus-container-default + 1.7.1 + provided + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 2.1 + test + + + + + + + org.codehaus.mojo + findbugs-maven-plugin + + findbugs-exclude.xml + + + + org.apache.rat + apache-rat-plugin + + + src/test/resources/directorywalker/**/* + src/test/resources/symlinks/**/* + src/test/resources/executable + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + + diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/Os.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/Os.java new file mode 100644 index 000000000..c4c5f33b9 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/Os.java @@ -0,0 +1,430 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + *

Condition that tests the OS type.

+ *

+ *

This class got copied over from Apache ANT. + * Even the version from plexus-utils was + * only an ANT fork!
+ * The last time it got copied was on 2011-08-12

+ *

+ *

When merging changes please take care of the special + * OS_FAMILY handling in this version of Os.java!

+ * + * @author Stefan Bodewig + * @author Magesh Umasankar + * @author Brian Fox + * @author Mark Struberg + * + */ +public class Os +{ + /** + * The OS Name. + */ + public static final String OS_NAME = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OA architecture. + */ + public static final String OS_ARCH = System.getProperty( "os.arch" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OS version. + */ + public static final String OS_VERSION = System.getProperty( "os.version" ).toLowerCase( Locale.ENGLISH ); + + /** + * The path separator. + */ + public static final String PATH_SEP = System.getProperty( "path.separator" ); + + /** + * system line separator , e.g. "\n" on unixoid systems and "\r\n" on Windows + */ + public static final String LINE_SEP = System.getProperty( "line.separator" ); + + /** + * OS Family + */ + public static final String OS_FAMILY = getOsFamily(); + + // store the valid families + private static final Set VALID_FAMILIES = getValidFamilies(); + + + /** + * OS family to look for + */ + private String family; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WIN9X = "win9x"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OPENVMS = "openvms"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * + * @see bugzilla issue + * @see HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + + /** + * The set of valid families. This methods initializes the set until + * VALID_FAMILIES constant is set. + * @return The set of families. + */ + public static Set getValidFamilies() + { + if ( VALID_FAMILIES != null ) + { + return VALID_FAMILIES; + } + + Set valid = new HashSet(); + valid.add( FAMILY_DOS ); + valid.add( FAMILY_MAC ); + valid.add( FAMILY_NETWARE ); + valid.add( FAMILY_NT ); + valid.add( FAMILY_OPENVMS ); + valid.add( FAMILY_OS2 ); + valid.add( FAMILY_OS400 ); + valid.add( FAMILY_TANDEM ); + valid.add( FAMILY_UNIX ); + valid.add( FAMILY_WIN9X ); + valid.add( FAMILY_WINDOWS ); + valid.add( FAMILY_ZOS ); + + return Collections.unmodifiableSet( valid ); + } + + /** + * Default constructor + */ + public Os() + { + //default + } + + /** + * Constructor that sets the family attribute + * + * @param family a String value + */ + public Os( String family ) + { + setFamily( family ); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
+ * Possible values:
+ *
    + *
  • dos
  • + *
  • mac
  • + *
  • netware
  • + *
  • os/2
  • + *
  • tandem
  • + *
  • unix
  • + *
  • windows
  • + *
  • win9x
  • + *
  • z/os
  • + *
  • os/400
  • + *
+ */ + private void setFamily( String f ) + { + family = f.toLowerCase( Locale.ENGLISH ); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * + * @return true if the os matches. + * @see Os#setFamily(String) + */ + boolean eval() + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * + * @param family the family to check for + * @return true if the OS matches + * + */ + public static boolean isFamily( String family ) + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * + */ + public static boolean isName( String name ) + { + return isOs( null, name, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * + */ + public static boolean isArch( String arch ) + { + return isOs( null, null, arch, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * + */ + public static boolean isVersion( String version ) + { + return isOs( null, null, null, version ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * + */ + private static boolean isOs( String family, String name, String arch, String version ) + { + boolean retValue = false; + + if ( family != null || name != null || arch != null || version != null ) + { + + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + + if ( family != null ) + { + + //windows probing logic relies on the word 'windows' in + //the OS + boolean isWindows = OS_NAME.contains( FAMILY_WINDOWS ); + boolean is9x = false; + boolean isNT = false; + if ( isWindows ) + { + //there are only four 9x platforms that we look for + is9x = + ( OS_NAME.contains( "95" ) || OS_NAME.contains( "98" ) || OS_NAME.contains( "me" ) + //wince isn't really 9x, but crippled enough to + //be a muchness. Ant doesnt run on CE, anyway. + || OS_NAME.contains( "ce" ) ); + isNT = !is9x; + } + if ( family.equals( FAMILY_WINDOWS ) ) + { + isFamily = isWindows; + } + else if ( family.equals( FAMILY_WIN9X ) ) + { + isFamily = isWindows && is9x; + } + else if ( family.equals( FAMILY_NT ) ) + { + isFamily = isWindows && isNT; + } + else if ( family.equals( FAMILY_OS2 ) ) + { + isFamily = OS_NAME.contains( FAMILY_OS2 ); + } + else if ( family.equals( FAMILY_NETWARE ) ) + { + isFamily = OS_NAME.contains( FAMILY_NETWARE ); + } + else if ( family.equals( FAMILY_DOS ) ) + { + isFamily = PATH_SEP.equals( ";" ) && !isFamily( FAMILY_NETWARE ); + } + else if ( family.equals( FAMILY_MAC ) ) + { + isFamily = OS_NAME.contains( FAMILY_MAC ) || OS_NAME.contains( DARWIN ); + } + else if ( family.equals( FAMILY_TANDEM ) ) + { + isFamily = OS_NAME.contains( "nonstop_kernel" ); + } + else if ( family.equals( FAMILY_UNIX ) ) + { + isFamily = PATH_SEP.equals( ":" ) && !isFamily( FAMILY_OPENVMS ) && ( !isFamily( FAMILY_MAC ) + || OS_NAME.endsWith( "x" ) || OS_NAME.contains( DARWIN ) ); + } + else if ( family.equals( FAMILY_ZOS ) ) + { + isFamily = OS_NAME.contains( FAMILY_ZOS ) || OS_NAME.contains( "os/390" ); + } + else if ( family.equals( FAMILY_OS400 ) ) + { + isFamily = OS_NAME.contains( FAMILY_OS400 ); + } + else if ( family.equals( FAMILY_OPENVMS ) ) + { + isFamily = OS_NAME.contains( FAMILY_OPENVMS ); + } + else + { + isFamily = OS_NAME.contains( family.toLowerCase( Locale.US ) ); + } + } + if ( name != null ) + { + isName = name.equals( OS_NAME ); + } + if ( arch != null ) + { + isArch = arch.equals( OS_ARCH ); + } + if ( version != null ) + { + isVersion = version.equals( OS_VERSION ); + } + retValue = isFamily && isName && isArch && isVersion; + } + return retValue; + } + + /** + * Helper method to determine the current OS family. + * + * @return name of current OS family. + */ + private static String getOsFamily() + { + Set families = getValidFamilies(); + + for ( String fam : families ) + { + if ( Os.isFamily( fam ) ) + { + return fam; + } + } + return null; + } + + /** + * Test if the given family String represents a valid Family + * + * @param family the os family + * @return true if 'family' represents a valid OS-Family, false otherwise. + */ + public static boolean isValidFamily( String family ) + { + return VALID_FAMILIES.contains( family ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/PathTool.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/PathTool.java new file mode 100644 index 000000000..28bc3083f --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/PathTool.java @@ -0,0 +1,355 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Path tool contains static methods to assist in determining path-related + * information such as relative paths. + *

+ * This class originally got developed at Apache Anakia and later maintained + * in maven-utils of Apache Maven-1. + * Some external fixes by Apache Committers have been applied later. + */ +public class PathTool +{ + + /** + * The constructor. + * + * @deprecated This is a utility class with only static methods. Don't create instances of it. + */ + @Deprecated + public PathTool() + { + } + + /** + * Determines the relative path of a filename from a base directory. + * This method is useful in building relative links within pages of + * a web site. It provides similar functionality to Anakia's + * $relativePath context variable. The arguments to + * this method may contain either forward or backward slashes as + * file separators. The relative path returned is formed using + * forward slashes as it is expected this path is to be used as a + * link in a web page (again mimicking Anakia's behavior). + *

+ * This method is thread-safe. + *
+ *

+     * PathTool.getRelativePath( null, null )                                   = ""
+     * PathTool.getRelativePath( null, "/usr/local/java/bin" )                  = ""
+     * PathTool.getRelativePath( "/usr/local/", null )                          = ""
+     * PathTool.getRelativePath( "/usr/local/", "/usr/local/java/bin" )         = ".."
+     * PathTool.getRelativePath( "/usr/local/", "/usr/local/java/bin/java.sh" ) = "../.."
+     * PathTool.getRelativePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) = ""
+     * 
+ * + * @param basedir The base directory. + * @param filename The filename that is relative to the base + * directory. + * @return The relative path of the filename from the base + * directory. This value is not terminated with a forward slash. + * A zero-length string is returned if: the filename is not relative to + * the base directory, basedir is null or zero-length, + * or filename is null or zero-length. + */ + public static String getRelativePath( @Nullable String basedir, @Nullable String filename ) + { + basedir = uppercaseDrive( basedir ); + filename = uppercaseDrive( filename ); + + /* + * Verify the arguments and make sure the filename is relative + * to the base directory. + */ + if ( basedir == null || basedir.length() == 0 || filename == null || filename.length() == 0 + || !filename.startsWith( basedir ) ) + { + return ""; + } + + /* + * Normalize the arguments. First, determine the file separator + * that is being used, then strip that off the end of both the + * base directory and filename. + */ + String separator = determineSeparator( filename ); + basedir = StringUtils.chompLast( basedir, separator ); + filename = StringUtils.chompLast( filename, separator ); + + /* + * Remove the base directory from the filename to end up with a + * relative filename (relative to the base directory). This + * filename is then used to determine the relative path. + */ + String relativeFilename = filename.substring( basedir.length() ); + + return determineRelativePath( relativeFilename, separator ); + } + + /** + * This method can calculate the relative path between two pathes on a file system. + *
+ *
+     * PathTool.getRelativeFilePath( null, null )                                   = ""
+     * PathTool.getRelativeFilePath( null, "/usr/local/java/bin" )                  = ""
+     * PathTool.getRelativeFilePath( "/usr/local", null )                           = ""
+     * PathTool.getRelativeFilePath( "/usr/local", "/usr/local/java/bin" )          = "java/bin"
+     * PathTool.getRelativeFilePath( "/usr/local", "/usr/local/java/bin/" )         = "java/bin"
+     * PathTool.getRelativeFilePath( "/usr/local/java/bin", "/usr/local/" )         = "../.."
+     * PathTool.getRelativeFilePath( "/usr/local/", "/usr/local/java/bin/java.sh" ) = "java/bin/java.sh"
+     * PathTool.getRelativeFilePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) = "../../.."
+     * PathTool.getRelativeFilePath( "/usr/local/", "/bin" )                        = "../../bin"
+     * PathTool.getRelativeFilePath( "/bin", "/usr/local/" )                        = "../usr/local"
+     * 
+ * Note: On Windows based system, the / character should be replaced by \ character. + * + * @param oldPath old path + * @param newPath new path + * @return a relative file path from oldPath. + */ + public static String getRelativeFilePath( final String oldPath, final String newPath ) + { + if ( StringUtils.isEmpty( oldPath ) || StringUtils.isEmpty( newPath ) ) + { + return ""; + } + + // normalise the path delimiters + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they dont match, no + // relative path + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + // one has a drive path element and the other doesnt, no relative + // path. + return null; + } + + String resultPath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + if ( newPath.endsWith( File.separator ) && !resultPath.endsWith( File.separator ) ) + { + return resultPath + File.separator; + } + + return resultPath; + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * Determines the relative path of a filename. For each separator + * within the filename (except the leading if present), append the + * "../" string to the return value. + * + * @param filename The filename to parse. + * @param separator The separator used within the filename. + * @return The relative path of the filename. This value is not + * terminated with a forward slash. A zero-length string is + * returned if: the filename is zero-length. + */ + @Nonnull private static String determineRelativePath( @Nonnull String filename, @Nonnull String separator ) + { + if ( filename.length() == 0 ) + { + return ""; + } + + /* + * Count the slashes in the relative filename, but exclude the + * leading slash. If the path has no slashes, then the filename + * is relative to the current directory. + */ + int slashCount = StringUtils.countMatches( filename, separator ) - 1; + if ( slashCount <= 0 ) + { + return "."; + } + + /* + * The relative filename contains one or more slashes indicating + * that the file is within one or more directories. Thus, each + * slash represents a "../" in the relative path. + */ + StringBuilder sb = new StringBuilder(); + for ( int i = 0; i < slashCount; i++ ) + { + sb.append( "../" ); + } + + /* + * Finally, return the relative path but strip the trailing + * slash to mimic Anakia's behavior. + */ + return StringUtils.chop( sb.toString() ); + } + + /** + * Helper method to determine the file separator (forward or + * backward slash) used in a filename. The slash that occurs more + * often is returned as the separator. + * + * @param filename The filename parsed to determine the file + * separator. + * @return The file separator used within filename. + * This value is either a forward or backward slash. + */ + private static String determineSeparator( String filename ) + { + int forwardCount = StringUtils.countMatches( filename, "/" ); + int backwardCount = StringUtils.countMatches( filename, "\\" ); + + return forwardCount >= backwardCount ? "/" : "\\"; + } + + /** + * Cygwin prefers lowercase drive letters, but other parts of maven use uppercase + * + * @param path old path + * @return String + */ + static String uppercaseDrive( @Nullable String path ) + { + if ( path == null ) + { + return null; + } + if ( path.length() >= 2 && path.charAt( 1 ) == ':' ) + { + path = Character.toUpperCase( path.charAt( 0 ) ) + path.substring( 1 ); + } + return path; + } + + @Nonnull private static String buildRelativePath( @Nonnull String toPath, @Nonnull String fromPath, + final char separatorChar ) + { + // use tokeniser to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialise the tokenisers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatevers left of newPath. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/PropertyUtils.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/PropertyUtils.java new file mode 100644 index 000000000..70e21ed24 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/PropertyUtils.java @@ -0,0 +1,218 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Static utility methods for loading properties. + */ +public class PropertyUtils +{ + + /** + * The constructor. + * + * @deprecated This is a utility class with only static methods. Don't create instances of it. + */ + @Deprecated + public PropertyUtils() + { + } + + /** + * @param url the URL which should be used to load the properties + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.net.URL)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code URL} is {@code null}. + */ + @Deprecated + public static java.util.Properties loadProperties( @Nonnull URL url ) + { + try ( InputStream in = url.openStream() ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * @param file the file from which the properties will be loaded + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.File)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code File} is {@code null}. + */ + @Deprecated + public static Properties loadProperties( @Nonnull File file ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param is {@link InputStream} + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.InputStream)} instead. This method + * should not be used as it suppresses exceptions silently when loading properties fails. + */ + @Deprecated + public static Properties loadProperties( @Nullable InputStream is ) + { + try + { + Properties result = new Properties(); + if ( is != null ) + { + try ( InputStream in = is ) + { + result.load( in ); + } + catch ( IOException e ) + { + // ignore + } + } + return result; + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from a given {@code URL}. + *

+ * If the given {@code URL} is {@code null} or the properties can't be read, an empty properties object is returned. + *

+ * + * @param url the {@code URL} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable URL url ) + { + + Properties properties = new Properties(); + if ( url != null ) + { + try ( InputStream in = url.openStream() ) + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + return properties; + } + + /** + * Loads {@code Properties} from a {@code File}. + *

+ * If the given {@code File} is {@code null} or the properties file can't be read, an empty properties object is + * returned. + *

+ * + * @param file the {@code File} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable File file ) + { + Properties properties = new Properties(); + if ( file != null ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + + return properties; + + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * If the given {@code InputStream} is {@code null} or the properties can't be read, an empty properties object is + * returned. In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param inputStream the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable InputStream inputStream ) + { + + Properties properties = new Properties(); + + if ( inputStream != null ) + { + try ( InputStream in = inputStream ) // reassign inputStream to autoclose + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + + return properties; + + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/ReaderFactory.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/ReaderFactory.java new file mode 100644 index 000000000..4ec103a91 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/ReaderFactory.java @@ -0,0 +1,213 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import org.apache.commons.io.input.XmlStreamReader; + +import javax.annotation.Nonnull; + + +/** + * Utility to create Readers from streams, with explicit encoding choice: platform default, + * XML, or specified. + * + * @author Hervé Boutemy + * @see java.nio.charset.Charset + * @see Supported encodings + */ +public class ReaderFactory +{ + /** + * ISO Latin Alphabet #1, also known as ISO-LATIN-1. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.ISO_8859_1} + */ + @Deprecated + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.US_ASCII} + */ + @Deprecated + public static final String US_ASCII = "US-ASCII"; + + /** + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either + * order accepted on input, big-endian used on output). + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.UTF_16} + */ + @Deprecated + public static final String UTF_16 = "UTF-16"; + + /** + * Sixteen-bit Unicode Transformation Format, big-endian byte order. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.UTF_16BE} + */ + @Deprecated + public static final String UTF_16BE = "UTF-16BE"; + + /** + * Sixteen-bit Unicode Transformation Format, little-endian byte order. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.UTF_16LE} + */ + @Deprecated + public static final String UTF_16LE = "UTF-16LE"; + + /** + * Eight-bit Unicode Transformation Format. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.UTF_8} + */ + @Deprecated + public static final String UTF_8 = "UTF-8"; + + /** + * The file.encoding System Property. + */ + public static final String FILE_ENCODING = System.getProperty( "file.encoding" ); + + /** + * Create a new Reader with XML encoding detection rules. + * + * @param in not null input stream + * @return an XML reader instance for the input stream + * @throws IOException if any + * @see XmlStreamReader + */ + public static Reader newXmlReader( @Nonnull InputStream in ) + throws IOException + { + return new XmlStreamReader( in ); + } + + /** + * Create a new Reader with XML encoding detection rules. + * + * @param file not null file + * @return an XML reader instance for the input file + * @throws IOException if any + * @see XmlStreamReader + */ + public static Reader newXmlReader( @Nonnull File file ) + throws IOException + { + return new XmlStreamReader( file ); + } + + /** + * Create a new Reader with XML encoding detection rules. + * + * @param url not null url + * @return an XML reader instance for the input URL + * @throws IOException if any + * @see XmlStreamReader + */ + public static Reader newXmlReader( @Nonnull URL url ) + throws IOException + { + return new XmlStreamReader( url ); + } + + /** + * Create a new Reader with default platform encoding. + * + * @param file not null file. + * @return a reader instance for the input file using the default platform character set + * @throws FileNotFoundException if any + * @see java.nio.charset.Charset#defaultCharset() + * @deprecated always specify an encoding. Do not depend on the default platform character set. + */ + @Deprecated + public static Reader newPlatformReader( @Nonnull File file ) + throws FileNotFoundException + { + return new FileReader( file ); + } + + /** + * Create a new Reader with specified encoding. + * + * @param in not null input stream + * @param encoding not null supported encoding + * @return a reader instance for the input stream using the given encoding + * @throws UnsupportedEncodingException if any + * @see Supported + * encodings + */ + public static Reader newReader( @Nonnull InputStream in, @Nonnull String encoding ) + throws UnsupportedEncodingException + { + return new InputStreamReader( in, encoding ); + } + + /** + * Create a new Reader with specified encoding. + * + * @param file not null file + * @param encoding not null supported encoding + * @return a reader instance for the input file using the given encoding + * @throws FileNotFoundException if any + * @throws UnsupportedEncodingException if any + * @see Supported + * encodings + */ + public static Reader newReader( @Nonnull File file, @Nonnull String encoding ) + throws FileNotFoundException, UnsupportedEncodingException + { + return new InputStreamReader( new FileInputStream( file ), encoding ); + } + + /** + * Create a new Reader with specified encoding. + * + * @param url not null URL + * @param encoding not null supported encoding + * @return a reader instance for the input URL using the given encoding + * @throws IOException if any + * @see Supported + * encodings + */ + public static Reader newReader( @Nonnull URL url, @Nonnull String encoding ) + throws IOException + { + return new InputStreamReader( url.openStream(), encoding ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/StringUtils.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/StringUtils.java new file mode 100644 index 000000000..b44a054f9 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/StringUtils.java @@ -0,0 +1,2551 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

Common String manipulation routines.

+ * + *

Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

+ * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

+ * + *

This constructor is public to permit tools that require a JavaBean + * manager to operate.

+ */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

+ * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

+ * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

Deletes all whitespace from a String.

+ * + *

Whitespace is defined by + * {@link Character#isWhitespace(char)}.

+ * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

Checks if a String is non null and is + * not empty (length > 0).

+ * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

Checks if a (trimmed) String is null or empty.

+ * + *

Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

+ * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

+ * Checks if a String is whitespace, empty ("") or null. + *

+ * + *
+     * StringUtils.isBlank(null)      = true
+     * StringUtils.isBlank("")        = true
+     * StringUtils.isBlank(" ")       = true
+     * StringUtils.isBlank("bob")     = false
+     * StringUtils.isBlank("  bob  ") = false
+     * 
+ * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

+ * Checks if a String is not empty (""), not null and not whitespace only. + *

+ * + *
+     * StringUtils.isNotBlank(null)      = false
+     * StringUtils.isNotBlank("")        = false
+     * StringUtils.isNotBlank(" ")       = false
+     * StringUtils.isNotBlank("bob")     = true
+     * StringUtils.isNotBlank("  bob  ") = true
+     * 
+ * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

Compares two Strings, returning true if they are equal.

+ * + *

nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

+ * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

Compares two Strings, returning true if they are equal ignoring + * the case.

+ * + *

Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

+ * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

Find the first index of any of a set of potential substrings.

+ * + *

null String will return -1.

+ * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

Find the latest index of any of a set of potential substrings.

+ * + *

null string will return -1.

+ * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

Gets a substring from the specified string avoiding exceptions.

+ * + *

A negative start position can be used to start n + * characters from the end of the String.

+ * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

Gets a substring from the specified String avoiding exceptions.

+ * + *

A negative start position can be used to start/end n + * characters from the end of the String.

+ * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

Gets the leftmost n characters of a String.

+ * + *

If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

+ * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

Gets the rightmost n characters of a String.

+ * + *

If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

+ * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

Gets n characters from the middle of a String.

+ * + *

If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

+ * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

Splits the provided text into a array, using whitespace as the + * separator.

+ * + *

The separator is not included in the returned String array.

+ * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

Splits the provided text into a array, based on a given separator.

+ * + *

The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

+ * + *

This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

+ * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

Concatenates elements of an array into a single String.

+ * + *

The difference from join is that concatenate has no delimiter.

+ * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

Joins the elements of the provided array into a single String + * containing the provided list of elements.

+ * + *

No delimiter is added before or after the list. A + * null separator is the same as a blank String.

+ * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

Joins the elements of the provided Iterator into + * a single String containing the provided elements.

+ * + *

No delimiter is added before or after the list. A + * null separator is the same as a blank String.

+ * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

Replace a char with another char inside a larger String, once.

+ * + *

A null reference passed to this method is a no-op.

+ * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

Replace all occurances of a char within another char.

+ * + *

A null reference passed to this method is a no-op.

+ * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

Replace a char with another char inside a larger String, + * for the first max values of the search char.

+ * + *

A null reference passed to this method is a no-op.

+ * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

Replace a String with another String inside a larger String, once.

+ * + *

A null reference passed to this method is a no-op.

+ * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

Replace all occurrences of a String within another String.

+ * + *

A null reference passed to this method is a no-op.

+ * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

Replace a String with another String inside a larger String, + * for the first max values of the search String.

+ * + *

A null reference passed to this method is a no-op.

+ * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

Overlay a part of a String with another String.

+ * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

Center a String in a larger String of size n.

+ * + *

Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

+ * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

Center a String in a larger String of size n.

+ * + *

Uses a supplied String as the value to buffer the String with.

+ * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

Remove the last newline, and everything after it from a String.

+ * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

Remove the last value of a supplied String, and everything after + * it from a String.

+ * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

Remove a newline if and only if it is at the end + * of the supplied String.

+ * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

Remove a value if and only if the String ends with that value.

+ * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

Remove everything and return the last value of a supplied String, and + * everything after it from a String.

+ * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

Remove the first value of a supplied String, and everything before it + * from a String.

+ * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

Remove and return everything before the first value of a + * supplied String from another String.

+ * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

Remove the last character from a String.

+ * + *

If the String ends in \r\n, then remove both + * of them.

+ * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

+ * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

Escapes any values it finds into their String form.

+ * + *

So a tab becomes the characters '\\' and + * 't'.

+ * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

Repeat a String n times to form a + * new string.

+ * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

Right pad a String with spaces.

+ * + *

The String is padded to the size of n.

+ * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

Right pad a String with a specified string.

+ * + *

The String is padded to the size of n.

+ * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

Left pad a String with spaces.

+ * + *

The String is padded to the size of n.

+ * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

Remove whitespace from the front and back of a String.

+ * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

Remove a specified String from the front and back of a + * String.

+ * + *

If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

+ * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

Strip whitespace from the front and back of every String + * in the array.

+ * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

Strip the specified delimiter from the front and back of + * every String in the array.

+ * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

Strip any of a supplied String from the end of a String.

+ * + *

If the strip String is null, whitespace is + * stripped.

+ * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

Strip any of a supplied String from the start of a String.

+ * + *

If the strip String is null, whitespace is + * stripped.

+ * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

Convert a String to upper case, null String + * returns null.

+ * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

Convert a String to lower case, null String + * returns null.

+ * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

Uncapitalise a String.

+ * + *

That is, convert the first character into lower-case. + * null is returned as null.

+ * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

Capitalise a String.

+ * + *

That is, convert the first character into title-case. + * null is returned as null.

+ * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

Swaps the case of String.

+ * + *

Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

+ * + *

null is returned as null.

+ * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

Capitalise all the words in a String.

+ * + *

Uses {@link Character#isWhitespace(char)} as a + * separator between words.

+ * + *

null will return null.

+ * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

Uncapitalise all the words in a string.

+ * + *

Uses {@link Character#isWhitespace(char)} as a + * separator between words.

+ * + *

null will return null.

+ * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

Get the String that is nested in between two instances of the + * same String.

+ * + *

If str is null, will + * return null.

+ * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

Get the String that is nested in between two Strings.

+ * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

How many times is the substring in the larger String.

+ * + *

null returns 0.

+ * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

Checks if the String contains only Unicode letters.

+ * + *

null will return false. + * An empty String will return true.

+ * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

Checks if the String contains only whitespace.

+ * + *

null will return false. An + * empty String will return true.

+ * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

Checks if the String contains only Unicode letters and + * space (' ').

+ * + *

null will return false. An + * empty String will return true.

+ * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

Checks if the String contains only Unicode letters or digits.

+ * + *

null will return false. An empty + * String will return true.

+ * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

Checks if the String contains only Unicode letters, digits + * or space (' ').

+ * + *

null will return false. An empty + * String will return true.

+ * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

Checks if the String contains only Unicode digits.

+ * + *

null will return false. + * An empty String will return true.

+ * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

+ * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

+ * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

Reverse a String.

+ * + *

null String returns null.

+ * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

Reverses a String that is delimited by a specific character.

+ * + *

The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

+ * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

Reverses an array.

+ * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

+ * Specifically: + *

+ * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

+ * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

+ * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

+ * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

+ * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
+     * removeAndHump( "this-is-it", %quot;-" );
+     * 
+ * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

+ * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

+ * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

Quote and escape a String with the given character, handling null.

+ * + *
+     * StringUtils.quoteAndEscape(null, *)    = null
+     * StringUtils.quoteAndEscape("", *)      = ""
+     * StringUtils.quoteAndEscape("abc", '"') = abc
+     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
+     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
+     * 
+ * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

Quote and escape a String with the given character, handling null.

+ * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

+ * + *

A null or empty ("") String will return false.

+ * + *
+     * StringUtils.contains(null, *)    = false
+     * StringUtils.contains("", *)      = false
+     * StringUtils.contains("abc", 'a') = true
+     * StringUtils.contains("abc", 'z') = false
+     * 
+ * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

+ * + *

A null String will return false.

+ * + *
+     * StringUtils.contains(null, *)     = false
+     * StringUtils.contains(*, null)     = false
+     * StringUtils.contains("", "")      = true
+     * StringUtils.contains("abc", "")   = true
+     * StringUtils.contains("abc", "a")  = true
+     * StringUtils.contains("abc", "z")  = false
+     * 
+ * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

Checks if String ends with a search String, handling null.

+ *

+ *

A null String will return false.

+ * + *
+     * StringUtils.endsWithIgnoreCase(null, *)     = false
+     * StringUtils.endsWithIgnoreCase(*, null)     = false
+     * StringUtils.endsWithIgnoreCase("", "")      = true
+     * StringUtils.endsWithIgnoreCase("abc", "")   = true
+     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
+     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
+     * 
+ * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/WriterFactory.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/WriterFactory.java new file mode 100644 index 000000000..e81cceed4 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/WriterFactory.java @@ -0,0 +1,194 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import org.apache.maven.shared.utils.xml.XmlStreamWriter; + + +import javax.annotation.Nonnull; + +/** + * Utility to create Writers, with explicit encoding choice: platform default, + * XML, or specified. + * + * @author Hervé Boutemy + * @see java.nio.charset.Charset + * @see Supported encodings + */ +public class WriterFactory +{ + /** + * ISO Latin Alphabet #1, also known as ISO-LATIN-1. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.ISO_8859_1} + */ + @Deprecated + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.US_ASCII} + */ + @Deprecated + public static final String US_ASCII = "US-ASCII"; + + /** + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either + * order accepted on input, big-endian used on output). + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.UTF_16} + */ + @Deprecated + public static final String UTF_16 = "UTF-16"; + + /** + * Sixteen-bit Unicode Transformation Format, big-endian byte order. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.UTF_16BE} + */ + @Deprecated + public static final String UTF_16BE = "UTF-16BE"; + + /** + * Sixteen-bit Unicode Transformation Format, little-endian byte order. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.UTF_16LE} + */ + @Deprecated + public static final String UTF_16LE = "UTF-16LE"; + + /** + * Eight-bit Unicode Transformation Format. + * Every implementation of the Java platform is required to support this character encoding. + * + * @deprecated use {@code java.nio.charset.StandardCharset.UTF_8} + */ + @Deprecated + public static final String UTF_8 = "UTF-8"; + + /** + * The file.encoding System Property. + */ + public static final String FILE_ENCODING = System.getProperty( "file.encoding" ); + + /** + * Create a new Writer with XML encoding detection rules. + * + * @param out not null output stream + * @return an XML writer instance for the output stream + * @throws IOException if any + * @see XmlStreamWriter + */ + public static XmlStreamWriter newXmlWriter( @Nonnull OutputStream out ) + throws IOException + { + return new XmlStreamWriter( out ); + } + + /** + * Create a new Writer with XML encoding detection rules. + * + * @param file not null file + * @return an XML writer instance for the output file + * @throws IOException if any + * @see XmlStreamWriter + */ + public static XmlStreamWriter newXmlWriter( @Nonnull File file ) + throws IOException + { + return new XmlStreamWriter( file ); + } + + /** + * Create a new Writer with default platform encoding. + * + * @param out not null output stream + * @return a writer instance for the output stream using the default platform charset + * @deprecated always specify an encoding. Do not depend on the default platform character set. + */ + @Deprecated + public static Writer newPlatformWriter( @Nonnull OutputStream out ) + { + return new OutputStreamWriter( out ); + } + + /** + * Create a new Writer with default platform encoding. + * + * @param file not null file + * @return a writer instance for the output file using the default platform charset + * @throws IOException if any + * @deprecated always specify an encoding. Do not depend on the default platform character set. + */ + @Deprecated + public static Writer newPlatformWriter( @Nonnull File file ) + throws IOException + { + return new FileWriter( file ); + } + + /** + * Create a new Writer with specified encoding. + * + * @param out not null output stream + * @param encoding not null supported encoding + * @return a writer instance for the output stream using the given encoding + * @throws UnsupportedEncodingException if any + * @see Supported + * encodings + */ + public static Writer newWriter( @Nonnull OutputStream out, @Nonnull String encoding ) + throws UnsupportedEncodingException + { + return new OutputStreamWriter( out, encoding ); + } + + /** + * Create a new Writer with specified encoding. + * + * @param file not null file + * @param encoding not null supported encoding + * @return a writer instance for the output file using the given encoding + * @throws UnsupportedEncodingException if any + * @throws FileNotFoundException if any + * @see Supported + * encodings + */ + public static Writer newWriter( @Nonnull File file, @Nonnull String encoding ) + throws UnsupportedEncodingException, FileNotFoundException + { + return newWriter( new FileOutputStream( file ), encoding ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/AbstractStreamHandler.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/AbstractStreamHandler.java new file mode 100644 index 000000000..979e86151 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/AbstractStreamHandler.java @@ -0,0 +1,62 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +/** + * @author Kristian Rosenvold + */ +class AbstractStreamHandler + extends Thread +{ + private volatile boolean done; + + private volatile boolean disabled; + + boolean isDone() + { + return done; + } + + public synchronized void waitUntilDone() + throws InterruptedException + { + while ( !isDone() ) + { + wait(); + } + } + + + boolean isDisabled() + { + return disabled; + } + + public void disable() + { + disabled = true; + } + + protected void setDone() + { + done = true; + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/Arg.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/Arg.java new file mode 100644 index 000000000..e0eccf3ac --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/Arg.java @@ -0,0 +1,52 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.File; + +/** + * + */ +public interface Arg +{ + /** + * @param value Set the value. + */ + void setValue( String value ); + + /** + * @param line The line of arguments. + */ + void setLine( String line ) throws CommandLineException; + + /** + * @param value The file to be set. + */ + void setFile( File value ); + + /** + * To mask the argument value when a command line ask to print his arguments. + * + * @param mask new state of the {@code maks} property + * @since 0.6 + */ + void setMask( boolean mask ); + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineCallable.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineCallable.java new file mode 100644 index 000000000..b4e458643 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineCallable.java @@ -0,0 +1,37 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.util.concurrent.Callable; + +/** + * Callable wrapper that exposes the proper exception type to the client. + * + * @author Kristian Rosenvold + */ +public interface CommandLineCallable + extends Callable +{ + /** + * {@inheritDoc} + */ + Integer call() + throws CommandLineException; +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineException.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineException.java new file mode 100644 index 000000000..9954e353e --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineException.java @@ -0,0 +1,49 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +/** + * @author Trygve Laugstøl + */ +public class CommandLineException + extends Exception +{ + /** + * + */ + private static final long serialVersionUID = 1344773066470228441L; + + /** + * @param message The message of the exception. + */ + public CommandLineException( String message ) + { + super( message ); + } + + /** + * @param message The message of the exception. + * @param cause The problem which caused the exception. + */ + public CommandLineException( String message, Throwable cause ) + { + super( message, cause ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineTimeOutException.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineTimeOutException.java new file mode 100644 index 000000000..c1f820911 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineTimeOutException.java @@ -0,0 +1,44 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +/** + * @author Olivier Lamy + * + */ +public class CommandLineTimeOutException + extends CommandLineException +{ + + /** + * + */ + private static final long serialVersionUID = 7322428741683224481L; + + /** + * @param message The message of the exception. + * @param cause The cause of the exception. + */ + public CommandLineTimeOutException( String message, Throwable cause ) + { + super( message, cause ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java new file mode 100644 index 000000000..2ffcfb241 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java @@ -0,0 +1,628 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; + +/** + * @author Trygve Laugstøl + */ +public abstract class CommandLineUtils +{ + + /** + * A {@code StreamConsumer} providing consumed lines as a {@code String}. + * + * @see #getOutput() + */ + public static class StringStreamConsumer + implements StreamConsumer + { + + private final StringBuffer string = new StringBuffer(); + + private static final String LS = System.getProperty( "line.separator", "\n" ); + + /** + * {@inheritDoc} + */ + @Override + public void consumeLine( String line ) + { + string.append( line ).append( LS ); + } + + /** + * @return The output. + */ + public String getOutput() + { + return string.toString(); + } + + } + + /** + * Number of milliseconds per second. + */ + private static final long MILLIS_PER_SECOND = 1000L; + + /** + * Number of nanoseconds per second. + */ + private static final long NANOS_PER_SECOND = 1000000000L; + + /** + * @param cl The command line {@link Commandline} + * @param systemOut {@link StreamConsumer} + * @param systemErr {@link StreamConsumer} + * @return return code. + * @throws CommandLineException in case of a problem. + */ + public static int executeCommandLine( @Nonnull Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr ) + throws CommandLineException + { + return executeCommandLine( cl, null, systemOut, systemErr, 0 ); + } + + /** + * @param cl The command line {@link Commandline} + * @param systemOut {@link StreamConsumer} + * @param systemErr {@link StreamConsumer} + * @param timeoutInSeconds The timeout. + * @return return code. + * @throws CommandLineException in case of a problem. + */ + public static int executeCommandLine( @Nonnull Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr, + int timeoutInSeconds ) + throws CommandLineException + { + return executeCommandLine( cl, null, systemOut, systemErr, timeoutInSeconds ); + } + + /** + * @param cl The command line {@link Commandline} + * @param systemIn {@link StreamConsumer} + * @param systemOut {@link StreamConsumer} + * @param systemErr {@link StreamConsumer} + * @return return code. + * @throws CommandLineException in case of a problem. + */ + public static int executeCommandLine( @Nonnull Commandline cl, InputStream systemIn, StreamConsumer systemOut, + StreamConsumer systemErr ) + throws CommandLineException + { + return executeCommandLine( cl, systemIn, systemOut, systemErr, 0 ); + } + + /** + * @param cl The command line to execute + * @param systemIn The input to read from, must be thread safe + * @param systemOut A consumer that receives output, must be thread safe + * @param systemErr A consumer that receives system error stream output, must be thread safe + * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout. + * @return A return value, see {@link Process#exitValue()} + * @throws CommandLineException or CommandLineTimeOutException if time out occurs + */ + public static int executeCommandLine( @Nonnull Commandline cl, InputStream systemIn, StreamConsumer systemOut, + StreamConsumer systemErr, int timeoutInSeconds ) + throws CommandLineException + { + return executeCommandLine( cl, systemIn, systemOut, systemErr, timeoutInSeconds, null ); + } + + /** + * @param cl The command line to execute + * @param systemIn The input to read from, must be thread safe + * @param systemOut A consumer that receives output, must be thread safe + * @param systemErr A consumer that receives system error stream output, must be thread safe + * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout. + * @param runAfterProcessTermination Optional callback to run after the process terminated or the the timeout was + * exceeded, but before waiting on the stream feeder and pumpers to finish. + * @return A return value, see {@link Process#exitValue()} + * @throws CommandLineException or CommandLineTimeOutException if time out occurs + */ + public static int executeCommandLine( @Nonnull Commandline cl, InputStream systemIn, StreamConsumer systemOut, + StreamConsumer systemErr, int timeoutInSeconds, + @Nullable Runnable runAfterProcessTermination ) + throws CommandLineException + { + return executeCommandLine( cl, systemIn, systemOut, systemErr, timeoutInSeconds, runAfterProcessTermination, + null ); + } + + /** + * @param cl The command line to execute + * @param systemIn The input to read from, must be thread safe + * @param systemOut A consumer that receives output, must be thread safe + * @param systemErr A consumer that receives system error stream output, must be thread safe + * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout. + * @param runAfterProcessTermination Optional callback to run after the process terminated or the the timeout was + * exceeded, but before waiting on the stream feeder and pumpers to finish. + * @param streamCharset Charset to use for reading streams + * @return A return value, see {@link Process#exitValue()} + * @throws CommandLineException or CommandLineTimeOutException if time out occurs + */ + public static int executeCommandLine( @Nonnull Commandline cl, InputStream systemIn, StreamConsumer systemOut, + StreamConsumer systemErr, int timeoutInSeconds, + @Nullable Runnable runAfterProcessTermination, + @Nullable final Charset streamCharset ) + throws CommandLineException + { + final CommandLineCallable future = + executeCommandLineAsCallable( cl, systemIn, systemOut, systemErr, timeoutInSeconds, + runAfterProcessTermination, streamCharset ); + return future.call(); + } + + /** + * Immediately forks a process, returns a callable that will block until process is complete. + * + * @param cl The command line to execute + * @param systemIn The input to read from, must be thread safe + * @param systemOut A consumer that receives output, must be thread safe + * @param systemErr A consumer that receives system error stream output, must be thread safe + * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout. + * @param runAfterProcessTermination Optional callback to run after the process terminated or the the timeout was + * @return A CommandLineCallable that provides the process return value, see {@link Process#exitValue()}. "call" + * must be called on this to be sure the forked process has terminated, no guarantees is made about + * any internal state before after the completion of the call statements + * @throws CommandLineException or CommandLineTimeOutException if time out occurs + */ + public static CommandLineCallable executeCommandLineAsCallable( @Nonnull final Commandline cl, + @Nullable final InputStream systemIn, + final StreamConsumer systemOut, + final StreamConsumer systemErr, + final int timeoutInSeconds, + @Nullable final Runnable runAfterProcessTermination ) + throws CommandLineException + { + return executeCommandLineAsCallable( cl, systemIn, systemOut, systemErr, timeoutInSeconds, + runAfterProcessTermination, null ); + } + + /** + * Immediately forks a process, returns a callable that will block until process is complete. + * + * @param cl The command line to execute + * @param systemIn The input to read from, must be thread safe + * @param systemOut A consumer that receives output, must be thread safe + * @param systemErr A consumer that receives system error stream output, must be thread safe + * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout. + * @param runAfterProcessTermination Optional callback to run after the process terminated or the the timeout was + * @param streamCharset Charset to use for reading streams + * @return A CommandLineCallable that provides the process return value, see {@link Process#exitValue()}. "call" + * must be called on this to be sure the forked process has terminated, no guarantees is made about + * any internal state before after the completion of the call statements + * @throws CommandLineException or CommandLineTimeOutException if time out occurs + */ + public static CommandLineCallable executeCommandLineAsCallable( @Nonnull final Commandline cl, + @Nullable final InputStream systemIn, + final StreamConsumer systemOut, + final StreamConsumer systemErr, + final int timeoutInSeconds, + @Nullable final Runnable runAfterProcessTermination, + @Nullable final Charset streamCharset ) + throws CommandLineException + { + //noinspection ConstantConditions + if ( cl == null ) + { + throw new IllegalArgumentException( "cl cannot be null." ); + } + + final Process p = cl.execute(); + + final Thread processHook = new Thread() + { + + { + this.setName( "CommandLineUtils process shutdown hook" ); + this.setContextClassLoader( null ); + } + + @Override + public void run() + { + p.destroy(); + } + + }; + + ShutdownHookUtils.addShutDownHook( processHook ); + + return new CommandLineCallable() + { + + @Override + public Integer call() + throws CommandLineException + { + StreamFeeder inputFeeder = null; + StreamPumper outputPumper = null; + StreamPumper errorPumper = null; + try + { + if ( systemIn != null ) + { + inputFeeder = new StreamFeeder( systemIn, p.getOutputStream() ); + inputFeeder.setName( "StreamFeeder-systemIn" ); + inputFeeder.start(); + } + + outputPumper = new StreamPumper( p.getInputStream(), systemOut ); + outputPumper.setName( "StreamPumper-systemOut" ); + outputPumper.start(); + + errorPumper = new StreamPumper( p.getErrorStream(), systemErr ); + errorPumper.setName( "StreamPumper-systemErr" ); + errorPumper.start(); + + int returnValue; + if ( timeoutInSeconds <= 0 ) + { + returnValue = p.waitFor(); + } + else + { + final long now = System.nanoTime(); + final long timeout = now + NANOS_PER_SECOND * timeoutInSeconds; + while ( isAlive( p ) && ( System.nanoTime() < timeout ) ) + { + // The timeout is specified in seconds. Therefore we must not sleep longer than one second + // but we should sleep as long as possible to reduce the number of iterations performed. + Thread.sleep( MILLIS_PER_SECOND - 1L ); + } + + if ( isAlive( p ) ) + { + throw new InterruptedException( String.format( "Process timed out after %d seconds.", + timeoutInSeconds ) ); + + } + + returnValue = p.exitValue(); + } + +// TODO Find out if waitUntilDone needs to be called using a try-finally construct. The method may throw an +// InterruptedException so that calls to waitUntilDone may be skipped. +// try +// { +// if ( inputFeeder != null ) +// { +// inputFeeder.waitUntilDone(); +// } +// } +// finally +// { +// try +// { +// outputPumper.waitUntilDone(); +// } +// finally +// { +// errorPumper.waitUntilDone(); +// } +// } + if ( inputFeeder != null ) + { + inputFeeder.waitUntilDone(); + } + + outputPumper.waitUntilDone(); + errorPumper.waitUntilDone(); + + if ( inputFeeder != null ) + { + inputFeeder.close(); + + if ( inputFeeder.getException() != null ) + { + throw new CommandLineException( "Failure processing stdin.", inputFeeder.getException() ); + } + } + + if ( outputPumper.getException() != null ) + { + throw new CommandLineException( "Failure processing stdout.", outputPumper.getException() ); + } + + if ( errorPumper.getException() != null ) + { + throw new CommandLineException( "Failure processing stderr.", errorPumper.getException() ); + } + + return returnValue; + } + catch ( InterruptedException ex ) + { + throw new CommandLineTimeOutException( "Error while executing external command, process killed.", + ex ); + + } + finally + { + if ( inputFeeder != null ) + { + inputFeeder.disable(); + } + if ( outputPumper != null ) + { + outputPumper.disable(); + } + if ( errorPumper != null ) + { + errorPumper.disable(); + } + + try + { + if ( runAfterProcessTermination != null ) + { + runAfterProcessTermination.run(); + } + } + finally + { + ShutdownHookUtils.removeShutdownHook( processHook ); + + try + { + processHook.run(); + } + finally + { + if ( inputFeeder != null ) + { + inputFeeder.close(); + } + } + } + } + } + + }; + } + + /** + * Gets the shell environment variables for this process. Note that the returned mapping from variable names to + * values will always be case-sensitive regardless of the platform, i.e. getSystemEnvVars().get("path") + * and getSystemEnvVars().get("PATH") will in general return different values. However, on platforms + * with case-insensitive environment variables like Windows, all variable names will be normalized to upper case. + * + * @return The shell environment variables, can be empty but never null. + * @see System#getenv() System.getenv() API, new in JDK 5.0, to get the same result + * since 2.0.2 System#getenv() will be used if available in the current running jvm. + */ + public static Properties getSystemEnvVars() + { + return getSystemEnvVars( !Os.isFamily( Os.FAMILY_WINDOWS ) ); + } + + /** + * Return the shell environment variables. If caseSensitive == true, then envar + * keys will all be upper-case. + * + * @param caseSensitive Whether environment variable keys should be treated case-sensitively. + * @return Properties object of (possibly modified) envar keys mapped to their values. + * @see System#getenv() System.getenv() API, new in JDK 5.0, to get the same result + * since 2.0.2 System#getenv() will be used if available in the current running jvm. + */ + public static Properties getSystemEnvVars( boolean caseSensitive ) + { + Map envs = System.getenv(); + return ensureCaseSensitivity( envs, caseSensitive ); + } + + private static boolean isAlive( Process p ) + { + if ( p == null ) + { + return false; + } + + try + { + p.exitValue(); + return false; + } + catch ( IllegalThreadStateException e ) + { + return true; + } + } + + /** + * @param toProcess The command line to translate. + * @return The array of translated parts. + * @throws CommandLineException in case of unbalanced quotes. + */ + public static String[] translateCommandline( String toProcess ) throws CommandLineException + { + if ( ( toProcess == null ) || ( toProcess.length() == 0 ) ) + { + return new String[0]; + } + + // parse with a simple finite state machine + + final int normal = 0; + final int inQuote = 1; + final int inDoubleQuote = 2; + boolean inEscape = false; + int state = normal; + final StringTokenizer tok = new StringTokenizer( toProcess, "\"\' \\", true ); + List tokens = new ArrayList(); + StringBuilder current = new StringBuilder(); + + while ( tok.hasMoreTokens() ) + { + String nextTok = tok.nextToken(); + switch ( state ) + { + case inQuote: + if ( "\'".equals( nextTok ) ) + { + if ( inEscape ) + { + current.append( nextTok ); + inEscape = false; + } + else + { + state = normal; + } + } + else + { + current.append( nextTok ); + inEscape = "\\".equals( nextTok ); + } + break; + case inDoubleQuote: + if ( "\"".equals( nextTok ) ) + { + if ( inEscape ) + { + current.append( nextTok ); + inEscape = false; + } + else + { + state = normal; + } + } + else + { + current.append( nextTok ); + inEscape = "\\".equals( nextTok ); + } + break; + default: + if ( "\'".equals( nextTok ) ) + { + if ( inEscape ) + { + inEscape = false; + current.append( nextTok ); + } + else + { + state = inQuote; + } + } + else if ( "\"".equals( nextTok ) ) + { + if ( inEscape ) + { + inEscape = false; + current.append( nextTok ); + } + else + { + state = inDoubleQuote; + } + } + else if ( " ".equals( nextTok ) ) + { + if ( current.length() != 0 ) + { + tokens.add( current.toString() ); + current.setLength( 0 ); + } + } + else + { + current.append( nextTok ); + inEscape = "\\".equals( nextTok ); + } + break; + } + } + + if ( current.length() != 0 ) + { + tokens.add( current.toString() ); + } + + if ( ( state == inQuote ) || ( state == inDoubleQuote ) ) + { + throw new CommandLineException( "unbalanced quotes in " + toProcess ); + } + + return tokens.toArray( new String[tokens.size()] ); + } + + /** + * @param line The line + * @return The concatenate lines. + */ + public static String toString( String... line ) + { + // empty path return empty string + if ( ( line == null ) || ( line.length == 0 ) ) + { + return ""; + } + + // path containing one or more elements + final StringBuilder result = new StringBuilder(); + for ( int i = 0; i < line.length; i++ ) + { + if ( i > 0 ) + { + result.append( ' ' ); + } + try + { + result.append( StringUtils.quoteAndEscape( line[i], '\"' ) ); + } + catch ( Exception e ) + { + System.err.println( "Error quoting argument: " + e.getMessage() ); + } + } + return result.toString(); + } + + static Properties ensureCaseSensitivity( Map envs, boolean preserveKeyCase ) + { + Properties envVars = new Properties(); + for ( Map.Entry entry : envs.entrySet() ) + { + envVars.put( !preserveKeyCase ? entry.getKey().toUpperCase( Locale.ENGLISH ) : entry.getKey(), + entry.getValue() ); + } + return envVars; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java new file mode 100644 index 000000000..db1377084 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java @@ -0,0 +1,528 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; +import org.apache.maven.shared.utils.cli.shell.BourneShell; +import org.apache.maven.shared.utils.cli.shell.CmdShell; +import org.apache.maven.shared.utils.cli.shell.CommandShell; +import org.apache.maven.shared.utils.cli.shell.Shell; + +/** + *

+ * Commandline objects help handling command lines specifying processes to + * execute. + *

+ *

+ * The class can be used to define a command line as nested elements or as a + * helper to define a command line by an application. + *

+ *

+ * + * <someelement>
+ *   <acommandline executable="/executable/to/run">
+ *     <argument value="argument 1" />
+ *     <argument line="argument_1 argument_2 argument_3" />
+ *     <argument value="argument 4" />
+ *   </acommandline>
+ * </someelement>
+ *
+ *

+ *

+ * The element someelement must provide a method + * createAcommandline which returns an instance of this class. + *

+ * + * @author thomas.haas@softwired-inc.com + * @author Stefan Bodewig + */ +public class Commandline + implements Cloneable +{ + private final List arguments = new Vector(); + + //protected Vector envVars = new Vector(); + // synchronized added to preserve synchronize of Vector class + private final Map envVars = Collections.synchronizedMap( new LinkedHashMap() ); + + private Shell shell; + + /** + * Create a new command line object. + * Shell is autodetected from operating system + * @param shell The shell instance. + */ + public Commandline( Shell shell ) + { + this.shell = shell; + } + + /** + * Create a new command line object. + * Shell is autodetected from operating system + * + * @param toProcess The command to process + */ + public Commandline( String toProcess ) throws CommandLineException + { + setDefaultShell(); + String[] tmp = CommandLineUtils.translateCommandline( toProcess ); + if ( ( tmp.length > 0 ) ) + { + setExecutable( tmp[0] ); + for ( int i = 1; i < tmp.length; i++ ) + { + createArg().setValue( tmp[i] ); + } + } + } + + /** + * Create a new command line object. + * Shell is autodetected from operating system + */ + public Commandline() + { + setDefaultShell(); + } + + /** + *

Sets the shell or command-line interpretor for the detected operating system, + * and the shell arguments.

+ */ + private void setDefaultShell() + { + //If this is windows set the shell to command.com or cmd.exe with correct arguments. + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + if ( Os.isFamily( Os.FAMILY_WIN9X ) ) + { + setShell( new CommandShell() ); + } + else + { + setShell( new CmdShell() ); + } + } + else + { + setShell( new BourneShell() ); + } + } + + /** + * Creates an argument object. + *

+ *

Each commandline object has at most one instance of the + * argument class. This method calls + * this.createArgument(false).

+ * + * @return the argument object. + */ + public Arg createArg() + { + return this.createArg( false ); + } + + /** + * Creates an argument object and adds it to our list of args. + *

+ *

Each commandline object has at most one instance of the + * argument class.

+ * + * @param insertAtStart if true, the argument is inserted at the + * beginning of the list of args, otherwise it is appended. + * @return The arguments. + */ + public Arg createArg( boolean insertAtStart ) + { + Arg argument = new Argument(); + if ( insertAtStart ) + { + arguments.add( 0, argument ); + } + else + { + arguments.add( argument ); + } + return argument; + } + + /** + * Sets the executable to run. + * @param executable The executable. + */ + public void setExecutable( String executable ) + { + shell.setExecutable( executable ); + } + + /** + * @return The executable. + */ + public String getExecutable() + { + + return shell.getExecutable(); + } + + /** + * @param line The arguments. + */ + public void addArguments( String... line ) + { + for ( String aLine : line ) + { + createArg().setValue( aLine ); + } + } + + /** + * Add an environment variable + * @param name The name of the environment variable. + * @param value The appropriate value. + */ + public void addEnvironment( String name, String value ) + { + //envVars.add( name + "=" + value ); + envVars.put( name, value ); + } + + /** + * Add system environment variables + */ + public void addSystemEnvironment() + { + Properties systemEnvVars = CommandLineUtils.getSystemEnvVars(); + + for ( Object o : systemEnvVars.keySet() ) + { + String key = (String) o; + if ( !envVars.containsKey( key ) ) + { + addEnvironment( key, systemEnvVars.getProperty( key ) ); + } + } + } + + /** + * Return the list of environment variables + * @return an array of all environment variables. + */ + public String[] getEnvironmentVariables() + { + addSystemEnvironment(); + String[] environmentVars = new String[envVars.size()]; + int i = 0; + for ( String name : envVars.keySet() ) + { + String value = envVars.get( name ); + environmentVars[i] = name + "=" + value; + i++; + } + return environmentVars; + } + + /** + * Returns the executable and all defined arguments. + * @return an array of all arguments incl. executable. + */ + public String[] getCommandline() + { + final String[] args = getArguments(); + String executable = getExecutable(); + + if ( executable == null ) + { + return args; + } + final String[] result = new String[args.length + 1]; + result[0] = executable; + System.arraycopy( args, 0, result, 1, args.length ); + return result; + } + + /** + * @return the shell, executable and all defined arguments without masking any arguments. + */ + private String[] getShellCommandline() + { + return getShellCommandline( false ) ; + } + + /** + * @param mask flag to mask any arguments (having his {@code mask} field to {@code true}). + * @return the shell, executable and all defined arguments with masking some arguments if + * {@code mask} parameter is on + */ + private String[] getShellCommandline( boolean mask ) + { + List shellCommandLine = getShell().getShellCommandLine( getArguments( mask ) ); + return shellCommandLine.toArray( new String[shellCommandLine.size()] ); + } + + /** + * Returns all arguments defined by addLine, + * addValue or the argument object. + * @return an array of arguments. + */ + public String[] getArguments() + { + return getArguments( false ); + } + + /** + * Returns all arguments defined by addLine, + * addValue or the argument object. + * + * @param mask flag to mask any arguments (having his {@code mask} field to {@code true}). + * @return an array of arguments. + */ + public String[] getArguments( boolean mask ) + { + List result = new ArrayList( arguments.size() * 2 ); + for ( Arg argument : arguments ) + { + Argument arg = (Argument) argument; + String[] s = arg.getParts(); + if ( s != null ) + { + if ( mask && ( arg.isMask() ) ) + { + // should be a key-pair argument + if ( s.length > 0 ) + { + + // use a masked copy + String[] copy = new String[s.length]; + Arrays.fill( copy, "*****" ); + s = copy; + } + } + Collections.addAll( result, s ); + } + } + + return result.toArray( new String[result.size()] ); + } + + /** {@inheritDoc} + */ + public String toString() + { + return StringUtils.join( getShellCommandline( true ), " " ); + } + + + /** {@inheritDoc} + */ + public Object clone() + { + throw new RuntimeException( "Do we ever clone a commandline?" ); +/* Commandline c = new Commandline( (Shell) shell.clone() ); + c.addArguments( getArguments() ); + return c;*/ + } + + /** + * Sets working directory. + * @param path The to be set as working directory. + */ + public void setWorkingDirectory( String path ) + { + shell.setWorkingDirectory( path ); + } + + /** + * Sets execution directory. + * @param workingDirectory The working directory. + */ + public void setWorkingDirectory( File workingDirectory ) + { + shell.setWorkingDirectory( workingDirectory ); + } + + /** + * @return The working directory. + */ + public File getWorkingDirectory() + { + return shell.getWorkingDirectory(); + } + + /** + * Clear out the arguments but leave the executable in place for another operation. + */ + public void clearArgs() + { + arguments.clear(); + } + + /** + * Executes the command. + * @return The process. + * @throws CommandLineException in case of errors. + */ + public Process execute() + throws CommandLineException + { + Process process; + + //addEnvironment( "MAVEN_TEST_ENVAR", "MAVEN_TEST_ENVAR_VALUE" ); + + String[] environment = getEnvironmentVariables(); + + File workingDir = shell.getWorkingDirectory(); + + try + { + if ( workingDir == null ) + { + process = Runtime.getRuntime().exec( getShellCommandline(), environment ); + } + else + { + if ( !workingDir.exists() ) + { + throw new CommandLineException( + "Working directory \"" + workingDir.getPath() + "\" does not exist!" ); + } + else if ( !workingDir.isDirectory() ) + { + throw new CommandLineException( + "Path \"" + workingDir.getPath() + "\" does not specify a directory." ); + } + + process = Runtime.getRuntime().exec( getShellCommandline(), environment, workingDir ); + } + } + catch ( IOException ex ) + { + throw new CommandLineException( "Error while executing process.", ex ); + } + + return process; + } + + /** + * Allows to set the shell to be used in this command line. + * + * @param shell the shell + */ + void setShell( Shell shell ) + { + this.shell = shell; + } + + /** + * Get the shell to be used in this command line. + * @return the shell. + */ + public Shell getShell() + { + return shell; + } + + /** + * + */ + public static class Argument + implements Arg + { + private String[] parts; + + private boolean mask; + + /** + * {@inheritDoc} + */ + public void setValue( String value ) + { + if ( value != null ) + { + parts = new String[]{ value }; + } + } + + /** + * {@inheritDoc} + */ + public void setLine( String line ) throws CommandLineException + { + if ( line == null ) + { + return; + } + try + { + parts = CommandLineUtils.translateCommandline( line ); + } + catch ( CommandLineException e ) + { + System.err.println( "Error translating Commandline." ); + throw( e ); + } + } + + /** + * {@inheritDoc} + */ + public void setFile( File value ) + { + parts = new String[]{ value.getAbsolutePath() }; + } + + /** + * {@inheritDoc} + */ + public void setMask( boolean mask ) + { + this.mask = mask; + } + + /** + * @return The parts. + */ + private String[] getParts() + { + return parts; + } + + /** + * @return true/false + */ + public boolean isMask() + { + return mask; + } + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/DefaultConsumer.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/DefaultConsumer.java new file mode 100644 index 000000000..59d99719d --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/DefaultConsumer.java @@ -0,0 +1,44 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.IOException; + +/** + * @author Emmanuel Venisse + */ +public class DefaultConsumer + implements StreamConsumer +{ + + /** + * {@inheritDoc} + */ + @Override + public void consumeLine( String line ) throws IOException + { + System.out.println( line ); + if ( System.out.checkError() ) + { + throw new IOException( String.format( "Failure writing line '%s' to stdout.", line ) ); + } + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/ShutdownHookUtils.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/ShutdownHookUtils.java new file mode 100644 index 000000000..d1ea56868 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/ShutdownHookUtils.java @@ -0,0 +1,73 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.security.AccessControlException; + +/** + * A shutdown hook that does not throw any exceptions upon container startup/shutdown or security manager + * restrictions. + * + * Incorrect usage of the hook itself may still throw an exception. + * + * @author Kristian Rosenvold + */ +public class ShutdownHookUtils +{ + + /** + * @param hook The thread hook. + */ + public static void addShutDownHook( Thread hook ) + { + try + { + Runtime.getRuntime().addShutdownHook( hook ); + } + catch ( IllegalStateException ignore ) + { + // ignore + } + catch ( AccessControlException ignore ) + { + // ignore + } + } + + /** + * @param hook The hook which should be removed. + */ + public static void removeShutdownHook( Thread hook ) + { + try + { + Runtime.getRuntime().removeShutdownHook( hook ); + } + catch ( IllegalStateException ignore ) + { + // ignore + } + catch ( AccessControlException ignore ) + { + // ignore + } + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java new file mode 100644 index 000000000..0cde961e1 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java @@ -0,0 +1,43 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.IOException; + +/** + * Works in concert with the StreamPumper class to + * allow implementations to gain access to the lines being + * "Pumped". + *

+ * Please note that implementations of this interface can be expected to be + * called from arbitrary threads and must therefore be threadsafe. + * + * @author Florin Vancea + * @author Paul Julius + */ +public interface StreamConsumer +{ + /** + * Called when the StreamPumper pumps a line from the Stream. + * @param line The line to be consumed. + * @throws IOException if consuming {@code line} fails. + */ + void consumeLine( String line ) throws IOException; +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java new file mode 100644 index 000000000..6f6723c40 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java @@ -0,0 +1,151 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Read from an InputStream and write the output to an OutputStream. + * + * @author Trygve Laugstøl + */ +class StreamFeeder + extends AbstractStreamHandler +{ + + private final AtomicReference input; + + private final AtomicReference output; + + private volatile Throwable exception; + + /** + * Create a new StreamFeeder + * + * @param input Stream to read from + * @param output Stream to write to + */ + StreamFeeder( InputStream input, OutputStream output ) + { + super(); + this.input = new AtomicReference( input ); + this.output = new AtomicReference( output ); + } + + @Override + public void run() + { + try + { + feed(); + } + catch ( Throwable e ) + { + // Catch everything so the streams will be closed and flagged as done. + if ( this.exception != null ) + { + this.exception = e; + } + } + finally + { + close(); + + synchronized ( this ) + { + notifyAll(); + } + } + } + + public void close() + { + setDone(); + final InputStream is = input.getAndSet( null ); + if ( is != null ) + { + try + { + is.close(); + } + catch ( IOException ex ) + { + if ( this.exception != null ) + { + this.exception = ex; + } + } + } + + final OutputStream os = output.getAndSet( null ); + if ( os != null ) + { + try + { + os.close(); + } + catch ( IOException ex ) + { + if ( this.exception != null ) + { + this.exception = ex; + } + } + } + } + + /** + * @since 3.2.0 + */ + public Throwable getException() + { + return this.exception; + } + + @SuppressWarnings( "checkstyle:innerassignment" ) + private void feed() + throws IOException + { + InputStream is = input.get(); + OutputStream os = output.get(); + boolean flush = false; + + if ( is != null && os != null ) + { + for ( int data; !isDone() && ( data = is.read() ) != -1; ) + { + if ( !isDisabled() ) + { + os.write( data ); + flush = true; + } + } + + if ( flush ) + { + os.flush(); + } + } + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java new file mode 100644 index 000000000..980bfb61a --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java @@ -0,0 +1,161 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; +import javax.annotation.Nullable; + +/** + * Class to pump the error stream during Process's runtime. Copied from the Ant built-in task. + * + * @author Florin Vancea + * @author Paul Julius + */ +public class StreamPumper + extends AbstractStreamHandler +{ + private final BufferedReader in; + + private final StreamConsumer consumer; + + private volatile Exception exception = null; + + private static final int SIZE = 1024; + + /** + * @param in {@link InputStream} + * @param consumer {@link StreamConsumer} + */ + public StreamPumper( InputStream in, StreamConsumer consumer ) + { + this( new InputStreamReader( in ), consumer ); + } + + /** + * @param in {@link InputStream} + * @param consumer {@link StreamConsumer} + * @param charset {@link Charset} + */ + public StreamPumper( InputStream in, StreamConsumer consumer, @Nullable Charset charset ) + { + this( null == charset ? new InputStreamReader( in ) : new InputStreamReader( in, charset ), consumer ); + } + + /** + * @param in {@link Reader} + * @param consumer {@link StreamConsumer} + */ + private StreamPumper( Reader in, StreamConsumer consumer ) + { + super(); + this.in = new BufferedReader( in, SIZE ); + this.consumer = consumer; + } + + /** run it. */ + public void run() + { + try + { + for ( String line = in.readLine(); line != null; line = in.readLine() ) + { + try + { + if ( exception == null ) + { + consumeLine( line ); + } + } + catch ( Exception t ) + { + exception = t; + } + } + } + catch ( IOException e ) + { + exception = e; + } + finally + { + try + { + in.close(); + } + catch ( final IOException e2 ) + { + if ( this.exception == null ) + { + this.exception = e2; + } + } + + synchronized ( this ) + { + setDone(); + + this.notifyAll(); + } + } + } + + /** + * flush. + * + * @deprecated As of 3.2.0, removed without replacement. + */ + @Deprecated + public void flush() + { + // Nothing to flush. + } + + /** + * Close it. + * + * @deprecated As of 3.2.0, removed without replacement. + */ + @Deprecated + public void close() + { + // Nothing to close. + } + + /** + * @return {@link Exception} + */ + public Exception getException() + { + return exception; + } + + private void consumeLine( String line ) throws IOException + { + if ( consumer != null && !isDisabled() ) + { + consumer.consumeLine( line ); + } + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/WriterStreamConsumer.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/WriterStreamConsumer.java new file mode 100644 index 000000000..b93ccb7ce --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/WriterStreamConsumer.java @@ -0,0 +1,56 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Writer; + +/** + * @author Jason van Zyl + * + */ +public class WriterStreamConsumer + implements StreamConsumer +{ + + private final BufferedWriter writer; + + /** + * @param writer {@link Writer} + */ + public WriterStreamConsumer( Writer writer ) + { + super(); + this.writer = new BufferedWriter( writer ); + } + + /** + * {@inheritDoc} + */ + @Override + public void consumeLine( String line ) throws IOException + { + this.writer.append( line ); + this.writer.newLine(); + this.writer.flush(); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/AbstractJavaTool.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/AbstractJavaTool.java new file mode 100644 index 000000000..22826802e --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/AbstractJavaTool.java @@ -0,0 +1,360 @@ +package org.apache.maven.shared.utils.cli.javatool; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; +import org.apache.maven.shared.utils.cli.CommandLineException; +import org.apache.maven.shared.utils.cli.CommandLineUtils; +import org.apache.maven.shared.utils.cli.Commandline; +import org.apache.maven.shared.utils.cli.StreamConsumer; +import org.codehaus.plexus.logging.AbstractLogEnabled; + +import java.io.File; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; + +/** + * Abstract implementation of a {@link JavaTool}. + * + * @author Tony Chemit + * @since 0.5 + * @param + */ +public abstract class AbstractJavaTool + extends AbstractLogEnabled + implements JavaTool +{ + + /** + * The java tool name to find out in the jdk. + */ + private final String javaToolName; + + /** + * The location of the java tool executable file. + */ + private String javaToolFile; + + /** + * Optional toolChain used to find java tool executable file. + */ + private Object toolchain; + + /** + * @param javaToolName The name of the java tool. + */ + protected AbstractJavaTool( String javaToolName ) + { + this.javaToolName = javaToolName; + } + + /** + * Create the command line object given the request. + * + * @param request User request on the java tool + * @param javaToolFileLocation Location of the java tool file to use + * @return the command line + * @throws JavaToolException if could not create the command line from the request + */ + protected abstract Commandline createCommandLine( Request request, String javaToolFileLocation ) + throws JavaToolException; + + /** + * {@inheritDoc} + */ + public String getJavaToolName() + { + return javaToolName; + } + + /** + * {@inheritDoc} + */ + public void setToolchain( Object toolchain ) + { + this.toolchain = toolchain; + } + + /** + * {@inheritDoc} + */ + public JavaToolResult execute( Request request ) + throws JavaToolException + { + + if ( javaToolFile == null ) + { + + // find the java tool file to use + try + { + javaToolFile = findJavaToolExecutable(); + } + catch ( Exception e ) + { + throw new JavaToolException( "Error finding " + javaToolName + " executable. Reason: " + e.getMessage(), + e ); + } + } + + // creates the command line from the given request + Commandline cli = createCommandLine( request, javaToolFile ); + + // execute it + JavaToolResult result = executeCommandLine( cli, request ); + + // return result + return result; + } + + /** + * @return {@link InputStream} + */ + protected InputStream createSystemInputStream() + { + InputStream systemIn = new InputStream() + { + + /** + * {@inheritDoc} + */ + public int read() + { + return -1; + } + + }; + return systemIn; + } + + /** + * @param cli {@link Commandline} + * @param request The request. + * @return {@link JavaToolRequest} + */ + protected JavaToolResult executeCommandLine( Commandline cli, Request request ) + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Executing: " + cli ); + } + + JavaToolResult result = createResult(); + + result.setCommandline( cli ); + + InputStream systemIn = createSystemInputStream(); + + StreamConsumer systemOut = createSystemOutStreamConsumer( request ); + + StreamConsumer systemErr = createSystemErrorStreamConsumer( request ); + + try + { + int resultCode = CommandLineUtils.executeCommandLine( cli, systemIn, systemOut, systemErr ); + + result.setExitCode( resultCode ); + } + catch ( CommandLineException e ) + { + result.setExecutionException( e ); + } + + return result; + } + + /** + * @param request The request. + * @return {@link StreamConsumer} + */ + protected StreamConsumer createSystemErrorStreamConsumer( Request request ) + { + StreamConsumer systemErr = request.getSystemErrorStreamConsumer(); + + if ( systemErr == null ) + { + systemErr = new StreamConsumer() + { + + /** + * {@inheritDoc} + */ + @Override + public void consumeLine( final String line ) + { + getLogger().warn( line ); + } + + }; + } + return systemErr; + } + + /** + * @param request The request. + * @return {@link StreamConsumer} + */ + protected StreamConsumer createSystemOutStreamConsumer( Request request ) + { + StreamConsumer systemOut = request.getSystemOutStreamConsumer(); + + if ( systemOut == null ) + { + + systemOut = new StreamConsumer() + { + + /** + * {@inheritDoc} + */ + @Override + public void consumeLine( final String line ) + { + getLogger().info( line ); + + } + + }; + } + return systemOut; + } + + /** + * @return The JavaToolResult. + */ + protected JavaToolResult createResult() + { + return new JavaToolResult(); + } + + /** + * @return The location of the java tool executable. + */ + protected String findJavaToolExecutable() + { + String executable = null; + + if ( toolchain != null ) + { + executable = findToolchainExecutable(); + } + + String command = javaToolName + ( Os.isFamily( Os.FAMILY_WINDOWS ) ? ".exe" : "" ); + + if ( executable == null ) + { + executable = findExecutable( command, System.getProperty( "java.home" ), "../bin", "bin", "../sh" ); + } + + if ( executable == null ) + { + + Map env = System.getenv(); + + String[] variables = { "JDK_HOME", "JAVA_HOME" }; + + for ( String variable : variables ) + { + executable = findExecutable( command, env.get( variable ), "bin", "sh" ); + if ( executable != null ) + { + break; + } + } + } + + if ( executable == null ) + { + executable = command; + } + + return executable; + } + + /** + * Run toolchain.findTool( javaToolName ); through reflection to avoid compile dependency on + * Maven core. + */ + private String findToolchainExecutable() + { + try + { + Method m = toolchain.getClass().getMethod( "findTool", String.class ); + return (String) m.invoke( toolchain, javaToolName ); + } + catch ( NoSuchMethodException e ) + { + // should not happen if toolchain is really a Toolchain object + getLogger().warn( "unexpected NoSuchMethodException", e ); + } + catch ( SecurityException e ) + { + // should not happen + getLogger().warn( "unexpected SecurityException", e ); + } + catch ( IllegalAccessException e ) + { + // should not happen + getLogger().warn( "unexpected IllegalAccessException", e ); + } + catch ( IllegalArgumentException e ) + { + // should not happen: parameter is the right type + getLogger().warn( "unexpected IllegalArgumentException", e ); + } + catch ( InvocationTargetException e ) + { + // not expected... + getLogger().warn( "unexpected InvocationTargetException", e ); + } + return null; + } + + /** + * Finds the specified command in any of the given sub directories of the specified JDK/JRE home directory. + * + * @param command The command to find, must not be null. + * @param homeDir The home directory to search in, may be null. + * @param subDirs The sub directories of the home directory to search in, must not be null. + * @return The (absolute) path to the command if found, null otherwise. + */ + private String findExecutable( String command, String homeDir, String... subDirs ) + { + String result = null; + if ( StringUtils.isNotEmpty( homeDir ) ) + { + for ( String subDir : subDirs ) + { + File file = new File( new File( homeDir, subDir ), command ); + + if ( file.isFile() ) + { + result = file.getAbsolutePath(); + break; + } + } + } + + return result; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/AbstractJavaToolRequest.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/AbstractJavaToolRequest.java new file mode 100644 index 000000000..6e18178ab --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/AbstractJavaToolRequest.java @@ -0,0 +1,75 @@ +package org.apache.maven.shared.utils.cli.javatool; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.cli.StreamConsumer; + +/** + * Abstract implementation of a {@link JavaToolRequest}. + * + * @author Tony Chemit + * @since 0.5 + */ +public class AbstractJavaToolRequest + implements JavaToolRequest +{ + + /** + * Optional system out stream consumer used by the commandline execution. + */ + private StreamConsumer systemOutStreamConsumer; + + /** + * Optional system error stream consumer used by the commandline execution. + */ + private StreamConsumer systemErrorStreamConsumer; + + /** + * {@inheritDoc} + */ + public StreamConsumer getSystemOutStreamConsumer() + { + return systemOutStreamConsumer; + } + + /** + * {@inheritDoc} + */ + public StreamConsumer getSystemErrorStreamConsumer() + { + return systemErrorStreamConsumer; + } + + /** + * {@inheritDoc} + */ + public void setSystemOutStreamConsumer( StreamConsumer systemOutStreamConsumer ) + { + this.systemOutStreamConsumer = systemOutStreamConsumer; + } + + /** + * {@inheritDoc} + */ + public void setSystemErrorStreamConsumer( StreamConsumer systemErrorStreamConsumer ) + { + this.systemErrorStreamConsumer = systemErrorStreamConsumer; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaTool.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaTool.java new file mode 100644 index 000000000..b4bc3a9a6 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaTool.java @@ -0,0 +1,72 @@ +package org.apache.maven.shared.utils.cli.javatool; + +/* + * 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. + */ + +/** + * Describes a java tool, means a executable available in the jdk. + *

+ * The name of the tool ({@link #getJavaToolName()}) reflects the name of the executable that should exists as an + * executable in the jdk, like {@code jarsigner, keytool, javadoc, ...}. + *

+ * An abstract implementation of the {@link JavaTool} named {@link AbstractJavaTool} use the command line API to execute + * any user requests of this tool. + * + * @author Tony Chemit + * @since 0.5 + * @param + */ +public interface JavaTool +{ + + /** + * Return the name of the java tool. This is exactly the name (without his extension) of the executable to + * find in the {@code jdk/bin} directory. + *

+ * For example: {@code jarsigner, keytool, javadoc, ...} + * + * @return the name of the java tool. + */ + String getJavaToolName(); + + /** + * Set an optional tool chain to find out the java tool executable location. + * + * @param toolchain optional tool chain to find out the java tool executable location. + * To avoid direct dependency on Maven core, this parameter is an Object that will be + * used as Toolchain through reflection + */ + void setToolchain( Object toolchain ); + + /** + * Execute the input request and then returns the result of the execution. + *

+ * If could not create the java tool invocation, a {@link JavaToolException} will be thrown. + *

+ * If execution fails, then the result will have a none-zero {@link JavaToolResult#getExitCode()} and his + * {@link JavaToolResult#getExecutionException()} will be filled with the error, otherwise the exist code will be + * zero. + * + * @param request the request to perform + * @return the result of the tool execution + * @throws JavaToolException if could not create the java tool invocation + */ + JavaToolResult execute( Request request ) + throws JavaToolException; +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolException.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolException.java new file mode 100644 index 000000000..fa10b33bb --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolException.java @@ -0,0 +1,55 @@ +package org.apache.maven.shared.utils.cli.javatool; + +/* + * 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. + */ + +/** + * Signals an error during the construction of the command line used to invoke java tool, e.g. illegal invocation + * arguments. + *

+ * This should not be confused with a failure of the invoked java tool build itself which will be reported by means of a + * non-zero exit code. + * + * @author Tony Chemit + * + * @see JavaToolResult#getExitCode() + * @since 0.5 + */ +public class JavaToolException + extends Exception +{ + private static final long serialVersionUID = 1L; + + /** + * @param message The message of the exception. + */ + public JavaToolException( String message ) + { + super( message ); + } + + /** + * @param message The message of the exception. + * @param cause The cause of the exception. + */ + public JavaToolException( String message, Throwable cause ) + { + super( message, cause ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolRequest.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolRequest.java new file mode 100644 index 000000000..7227774c0 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolRequest.java @@ -0,0 +1,66 @@ +package org.apache.maven.shared.utils.cli.javatool; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.cli.StreamConsumer; + +/** + * Specifies the minimum parameters used to control a {@link JavaTool} invocation. + * + * @author Tony Chemit + * @since 0.5 + */ +public interface JavaToolRequest +{ + + /** + * Gets the value of the {@code systemOutStreamConsumer} field. + *

+ * This option field if filled is used by the commandline tool to consume system ouput stream of the jarsigner + * command. + * + * @return the value of the {@code systemOutStreamConsumer} field. + */ + StreamConsumer getSystemOutStreamConsumer(); + + /** + * Gets the value of the {@code systemErrorStreamConsumer} field. + *

+ * This option field if filled is used by the commandline tool to consume system error stream of the jarsigner + * command. + * + * @return the value of the {@code systemErrorStreamConsumer} field. + */ + StreamConsumer getSystemErrorStreamConsumer(); + + /** + * Sets the new given value to the field {@code systemOutStreamConsumer} of the request. + * + * @param systemOutStreamConsumer the new value of the field {@code systemOutStreamConsumer}. + */ + void setSystemOutStreamConsumer( StreamConsumer systemOutStreamConsumer ); + + /** + * Sets the new given value to the field {@code systemErrorStreamConsumer} of the request. + * + * @param systemErrorStreamConsumer the new value of the field {@code systemErrorStreamConsumer}. + */ + void setSystemErrorStreamConsumer( StreamConsumer systemErrorStreamConsumer ); +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolResult.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolResult.java new file mode 100644 index 000000000..f3a6a77ec --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/javatool/JavaToolResult.java @@ -0,0 +1,110 @@ +package org.apache.maven.shared.utils.cli.javatool; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.cli.CommandLineException; +import org.apache.maven.shared.utils.cli.Commandline; + +/** + * Describes the result of a {@link JavaTool} invocation. + * + * @author Tony Chemit + * @since 0.5 + */ +public class JavaToolResult +{ + /** + * The exception that prevented to execute the command line, will be null if jarSigner could be + * successfully started. + */ + private CommandLineException executionException; + + /** + * The exit code reported by the Maven invocation. + */ + private int exitCode = Integer.MIN_VALUE; + + /** + * The command line used to obtain this result. + */ + private Commandline commandline; + + /** + * Gets the exit code from the tool invocation. A non-zero value indicates a build failure. Note: + * This value is undefined if {@link #getExecutionException()} reports an exception. + * + * @return The exit code from the tool invocation. + */ + public int getExitCode() + { + return exitCode; + } + + /** + * Gets the command line used. + * + * @return The command line used + */ + public Commandline getCommandline() + { + return commandline; + } + + /** + * Gets the exception that possibly occurred during the execution of the command line. + * + * @return The exception that prevented to invoke tool or null if the command line was successfully + * processed by the operating system. + */ + public CommandLineException getExecutionException() + { + return executionException; + } + + /** + * Sets the exit code reported by the tool invocation. + * + * @param exitCode The exit code reported by the tool invocation. + */ + public void setExitCode( int exitCode ) + { + this.exitCode = exitCode; + } + + /** + * Sets the exception that prevented to execute the command line. + * + * @param executionException The exception that prevented to execute the command line, may be null. + */ + public void setExecutionException( CommandLineException executionException ) + { + this.executionException = executionException; + } + + /** + * Set the commandline used to obtain this result. + * + * @param commandline the commandline used to obtain this result + */ + public void setCommandline( Commandline commandline ) + { + this.commandline = commandline; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java new file mode 100644 index 000000000..e3af66512 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java @@ -0,0 +1,138 @@ +package org.apache.maven.shared.utils.cli.shell; + +/* + * 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. + */ + + +import java.util.ArrayList; +import java.util.List; +import org.apache.maven.shared.utils.Os; + +/** + * @author Jason van Zyl + */ +public class BourneShell + extends Shell +{ + + /** + * Create instance of BourneShell. + */ + public BourneShell() + { + setUnconditionalQuoting( true ); + setShellCommand( "/bin/sh" ); + setArgumentQuoteDelimiter( '\'' ); + setExecutableQuoteDelimiter( '\'' ); + setSingleQuotedArgumentEscaped( true ); + setSingleQuotedExecutableEscaped( false ); + setQuotedExecutableEnabled( true ); + } + + /** + * {@inheritDoc} + */ + public String getExecutable() + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + return super.getExecutable(); + } + + return quoteOneItem( super.getExecutable(), true ); + } + + /** {@inheritDoc} */ + public List getShellArgsList() + { + List shellArgs = new ArrayList(); + List existingShellArgs = super.getShellArgsList(); + + if ( ( existingShellArgs != null ) && !existingShellArgs.isEmpty() ) + { + shellArgs.addAll( existingShellArgs ); + } + + shellArgs.add( "-c" ); + + return shellArgs; + } + + /** {@inheritDoc} */ + public String[] getShellArgs() + { + String[] shellArgs = super.getShellArgs(); + if ( shellArgs == null ) + { + shellArgs = new String[0]; + } + + if ( ( shellArgs.length > 0 ) && !shellArgs[shellArgs.length - 1].equals( "-c" ) ) + { + String[] newArgs = new String[shellArgs.length + 1]; + + System.arraycopy( shellArgs, 0, newArgs, 0, shellArgs.length ); + newArgs[shellArgs.length] = "-c"; + + shellArgs = newArgs; + } + + return shellArgs; + } + + /** {@inheritDoc} */ + protected String getExecutionPreamble() + { + if ( getWorkingDirectoryAsString() == null ) + { + return null; + } + + String dir = getWorkingDirectoryAsString(); + + return "cd " + quoteOneItem( dir, false ) + " && "; + } + + /** + *

Unify quotes in a path for the Bourne Shell.

+ *

+ *

+     * BourneShell.quoteOneItem(null)                       = null
+     * BourneShell.quoteOneItem("")                         = ''
+     * BourneShell.quoteOneItem("/test/quotedpath'abc")     = '/test/quotedpath'"'"'abc'
+     * BourneShell.quoteOneItem("/test/quoted path'abc")    = '/test/quoted pat'"'"'habc'
+     * BourneShell.quoteOneItem("/test/quotedpath\"abc")    = '/test/quotedpath"abc'
+     * BourneShell.quoteOneItem("/test/quoted path\"abc")   = '/test/quoted path"abc'
+     * BourneShell.quoteOneItem("/test/quotedpath\"'abc")   = '/test/quotedpath"'"'"'abc'
+     * BourneShell.quoteOneItem("/test/quoted path\"'abc")  = '/test/quoted path"'"'"'abc'
+     * 
+ * + * @param path not null path. + * @return the path unified correctly for the Bourne shell. + */ + protected String quoteOneItem( String path, boolean isExecutable ) + { + if ( path == null ) + { + return null; + } + + return "'" + path.replace( "'", "'\"'\"'" ) + "'"; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java new file mode 100644 index 000000000..04aa6de1d --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java @@ -0,0 +1,97 @@ +package org.apache.maven.shared.utils.cli.shell; + +/* + * 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. + */ + +import java.util.Arrays; +import java.util.List; + +/** + * Implementation to call the CMD Shell present on Windows NT, 2000, XP, 7, 8, and 10. + * + * @author Carlos Sanchez + * + */ +public class CmdShell + extends Shell +{ + /** + * Create an instance of CmdShell. + */ + public CmdShell() + { + setShellCommand( "cmd.exe" ); + setQuotedExecutableEnabled( true ); + setShellArgs( new String[]{ "/X", "/C" } ); + } + + /** + *

+ * Specific implementation that quotes all the command line. + *

+ *

+ * Workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468220 + *

+ *

+ * From cmd.exe /? output: + *

+ *

+ *

+     *      If /C or /K is specified, then the remainder of the command line after
+     *      the switch is processed as a command line, where the following logic is
+     *      used to process quote (") characters:
+     *
+     *      1.  If all of the following conditions are met, then quote characters
+     *      on the command line are preserved:
+     *
+     *      - no /S switch
+     *      - exactly two quote characters
+     *      - no special characters between the two quote characters,
+     *      where special is one of: &<>()@ˆ|
+     *      - there are one or more whitespace characters between the
+     *      the two quote characters
+     *      - the string between the two quote characters is the name
+     *      of an executable file.
+     *
+     *      2.  Otherwise, old behavior is to see if the first character is
+     *      a quote character and if so, strip the leading character and
+     *      remove the last quote character on the command line, preserving
+     *      any text after the last quote character.
+     * 
+ *

+ *

+ * Always quoting the entire command line, regardless of these conditions + * appears to make Windows processes invoke successfully. + *

+ * + * @param executable The executable. + * @param arguments The arguments for the executable. + * @return The resulting command line. + */ + public List getCommandLine( String executable, String... arguments ) + { + StringBuilder sb = new StringBuilder(); + sb.append( '"' ); + sb.append( super.getCommandLine( executable, arguments ).get( 0 ) ); + sb.append( '"' ); + + return Arrays.asList( sb.toString() ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/CommandShell.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/CommandShell.java new file mode 100644 index 000000000..ba85633bf --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/CommandShell.java @@ -0,0 +1,42 @@ +package org.apache.maven.shared.utils.cli.shell; + +/* + * 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. + */ + + +/** + * Implementation to call the Command.com Shell present on Windows 95, 98 and Me + * + * @author Carlos Sanchez + * @deprecated Windows ME is long dead. Update to Windows 10 and use {@link CmdShell}. + */ +@Deprecated +public class CommandShell + extends Shell +{ + /** + * Create an instance. + */ + public CommandShell() + { + setShellCommand( "command.com" ); + setShellArgs( new String[]{ "/C" } ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java new file mode 100644 index 000000000..02681086f --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java @@ -0,0 +1,418 @@ +package org.apache.maven.shared.utils.cli.shell; + +/* + * 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. + */ + + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.maven.shared.utils.StringUtils; + +/** + * Class that abstracts the Shell functionality, + * with subclasses for shells that behave particularly, like

+ * + *

    + *
  • command.com
  • + *
  • cmd.exe
  • + *
+ * + * @author Carlos Sanchez + * + */ +public class Shell + implements Cloneable +{ + private static final char[] DEFAULT_QUOTING_TRIGGER_CHARS = { ' ' }; + + private String shellCommand; + + private final List shellArgs = new ArrayList(); + + private boolean quotedArgumentsEnabled = true; + + private boolean unconditionalQuoting = false; + + private String executable; + + private String workingDir; + + private boolean quotedExecutableEnabled = true; + + private boolean singleQuotedArgumentEscaped = false; + + private boolean singleQuotedExecutableEscaped = false; + + private char argQuoteDelimiter = '\"'; + + private char exeQuoteDelimiter = '\"'; + + /** + * Set the command to execute the shell (e.g. COMMAND.COM, /bin/bash,...). + * + * @param shellCommand the command + */ + void setShellCommand( String shellCommand ) + { + this.shellCommand = shellCommand; + } + + /** + * Get the command to execute the shell. + * + * @return the command + */ + String getShellCommand() + { + return shellCommand; + } + + /** + * Set the shell arguments when calling a command line (not the executable arguments) + * (e.g. /X /C for CMD.EXE). + * + * @param shellArgs the arguments to the shell + */ + void setShellArgs( String[] shellArgs ) + { + this.shellArgs.clear(); + this.shellArgs.addAll( Arrays.asList( shellArgs ) ); + } + + /** + * Get the shell arguments + * + * @return the arguments + */ + String[] getShellArgs() + { + if ( shellArgs.isEmpty() ) + { + return null; + } + else + { + return shellArgs.toArray( new String[0] ); + } + } + + protected String quoteOneItem( String inputString, boolean isExecutable ) + { + char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() ); + return StringUtils.quoteAndEscape( + inputString, + isExecutable ? getExecutableQuoteDelimiter() : getArgumentQuoteDelimiter(), + escapeChars, + getQuotingTriggerChars(), + '\\', + unconditionalQuoting + ); + } + + /** + * Get the command line for the provided executable and arguments in this shell + * + * @param executableParameter executable that the shell has to call + * @param argumentsParameter arguments for the executable, not the shell + * @return list with one String object with executable and arguments quoted as needed + */ + List getCommandLine( String executableParameter, String... argumentsParameter ) + { + return getRawCommandLine( executableParameter, argumentsParameter ); + } + + /** + * @param executableParameter Executable + * @param argumentsParameter the arguments for the executable + * @return the list on command line + */ + List getRawCommandLine( String executableParameter, String... argumentsParameter ) + { + List commandLine = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + + if ( executableParameter != null ) + { + String preamble = getExecutionPreamble(); + if ( preamble != null ) + { + sb.append( preamble ); + } + + if ( isQuotedExecutableEnabled() ) + { + sb.append( quoteOneItem( executableParameter, true ) ); + } + else + { + sb.append( executableParameter ); + } + } + for ( String argument : argumentsParameter ) + { + if ( sb.length() > 0 ) + { + sb.append( ' ' ); + } + + if ( isQuotedArgumentsEnabled() ) + { + sb.append( quoteOneItem( argument, false ) ); + } + else + { + sb.append( argument ); + } + } + + commandLine.add( sb.toString() ); + + return commandLine; + } + + char[] getQuotingTriggerChars() + { + return DEFAULT_QUOTING_TRIGGER_CHARS; + } + + String getExecutionPreamble() + { + return null; + } + + char[] getEscapeChars( boolean includeSingleQuote, boolean includeDoubleQuote ) + { + StringBuilder buf = new StringBuilder( 2 ); + if ( includeSingleQuote ) + { + buf.append( '\'' ); + } + + if ( includeDoubleQuote ) + { + buf.append( '\"' ); + } + + char[] result = new char[buf.length()]; + buf.getChars( 0, buf.length(), result, 0 ); + + return result; + } + + /** + * @return false in all cases + */ + protected boolean isDoubleQuotedArgumentEscaped() + { + return false; + } + + /** + * @return {@link #singleQuotedArgumentEscaped} + */ + protected boolean isSingleQuotedArgumentEscaped() + { + return singleQuotedArgumentEscaped; + } + + boolean isDoubleQuotedExecutableEscaped() + { + return false; + } + + boolean isSingleQuotedExecutableEscaped() + { + return singleQuotedExecutableEscaped; + } + + /** + * @param argQuoteDelimiterParameter {@link #argQuoteDelimiter} + */ + void setArgumentQuoteDelimiter( char argQuoteDelimiterParameter ) + { + this.argQuoteDelimiter = argQuoteDelimiterParameter; + } + + char getArgumentQuoteDelimiter() + { + return argQuoteDelimiter; + } + + /** + * @param exeQuoteDelimiterParameter {@link #exeQuoteDelimiter} + */ + void setExecutableQuoteDelimiter( char exeQuoteDelimiterParameter ) + { + this.exeQuoteDelimiter = exeQuoteDelimiterParameter; + } + + char getExecutableQuoteDelimiter() + { + return exeQuoteDelimiter; + } + + /** + * Get the full command line to execute, including shell command, shell arguments, + * executable and executable arguments + * + * @param arguments arguments for the executable, not the shell + * @return List of String objects, whose array version is suitable to be used as argument + * of Runtime.getRuntime().exec() + */ + public List getShellCommandLine( String... arguments ) + { + + List commandLine = new ArrayList<>(); + + if ( getShellCommand() != null ) + { + commandLine.add( getShellCommand() ); + } + + if ( getShellArgs() != null ) + { + commandLine.addAll( getShellArgsList() ); + } + + commandLine.addAll( getCommandLine( executable, arguments ) ); + + return commandLine; + + } + + List getShellArgsList() + { + return shellArgs; + } + + /** + * @param quotedArgumentsEnabled {@link #quotedArgumentsEnabled} + */ + public void setQuotedArgumentsEnabled( boolean quotedArgumentsEnabled ) + { + this.quotedArgumentsEnabled = quotedArgumentsEnabled; + } + + boolean isQuotedArgumentsEnabled() + { + return quotedArgumentsEnabled; + } + + void setQuotedExecutableEnabled( boolean quotedExecutableEnabled ) + { + this.quotedExecutableEnabled = quotedExecutableEnabled; + } + + boolean isQuotedExecutableEnabled() + { + return quotedExecutableEnabled; + } + + /** + * Sets the executable to run. + * @param executable The executable. + */ + public void setExecutable( String executable ) + { + if ( ( executable == null ) || ( executable.length() == 0 ) ) + { + return; + } + this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + + /** + * @return The executable. + */ + public String getExecutable() + { + return executable; + } + + /** + * Sets execution directory. + * @param path The path which should be used as working directory. + */ + public void setWorkingDirectory( String path ) + { + if ( path != null ) + { + this.workingDir = path; + } + } + + /** + * Sets execution directory. + * + * @param workingDirectory the working directory + */ + public void setWorkingDirectory( File workingDirectory ) + { + if ( workingDirectory != null ) + { + this.workingDir = workingDirectory.getAbsolutePath(); + } + } + + /** + * @return the working directory + */ + public File getWorkingDirectory() + { + return workingDir == null ? null : new File( workingDir ); + } + + String getWorkingDirectoryAsString() + { + return workingDir; + } + + /** {@inheritDoc} */ + public Object clone() + { + throw new RuntimeException( "Do we ever clone this?" ); +/* Shell shell = new Shell(); + shell.setExecutable( getExecutable() ); + shell.setWorkingDirectory( getWorkingDirectory() ); + shell.setShellArgs( getShellArgs() ); + return shell;*/ + } + + void setSingleQuotedArgumentEscaped( boolean singleQuotedArgumentEscaped ) + { + this.singleQuotedArgumentEscaped = singleQuotedArgumentEscaped; + } + + void setSingleQuotedExecutableEscaped( boolean singleQuotedExecutableEscaped ) + { + this.singleQuotedExecutableEscaped = singleQuotedExecutableEscaped; + } + + public boolean isUnconditionalQuoting() + { + return unconditionalQuoting; + } + + public void setUnconditionalQuoting( boolean unconditionalQuoting ) + { + this.unconditionalQuoting = unconditionalQuoting; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/ClassMap.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/ClassMap.java new file mode 100644 index 000000000..19b17322d --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/ClassMap.java @@ -0,0 +1,538 @@ +package org.apache.maven.shared.utils.introspection; + +/* + * 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. + */ + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Hashtable; +import java.util.Map; + +/** + * A cache of introspection information for a specific class instance. + * Keys {@link java.lang.reflect.Method} objects by a concatenation of the + * method name and the names of classes that make up the parameters. + * + * @author Jason van Zyl + * @author Bob McWhirter + * @author Attila Szegedi + * @author Geir Magnusson Jr. + * + */ +public class ClassMap +{ + private static final class CacheMiss + { + } + + private static final CacheMiss CACHE_MISS = new CacheMiss(); + + private static final Object OBJECT = new Object(); + + /** + * Class passed into the constructor used to as + * the basis for the Method map. + */ + + private final Class clazz; + + /** + * Cache of Methods, or CACHE_MISS, keyed by method + * name and actual arguments used to find it. + */ + private final Map methodCache = new Hashtable(); + + private MethodMap methodMap = new MethodMap(); + + /** + * Standard constructor + * @param clazz The class. + */ + public ClassMap( Class clazz ) + { + this.clazz = clazz; + populateMethodCache(); + } + + /** + * @return the class object whose methods are cached by this map. + */ + Class getCachedClass() + { + return clazz; + } + + /** + * Find a Method using the methodKey + * provided. + *

+ * Look in the methodMap for an entry. If found, + * it'll either be a CACHE_MISS, in which case we + * simply give up, or it'll be a Method, in which + * case, we return it. + *

+ * If nothing is found, then we must actually go + * and introspect the method from the MethodMap. + * @param name Method name. + * @param params Method parameters. + * @return The found method. + * @throws MethodMap.AmbiguousException in case of duplicate methods. + */ + public Method findMethod( String name, Object... params ) + throws MethodMap.AmbiguousException + { + String methodKey = makeMethodKey( name, params ); + Object cacheEntry = methodCache.get( methodKey ); + + if ( cacheEntry == CACHE_MISS ) + { + return null; + } + + if ( cacheEntry == null ) + { + try + { + cacheEntry = methodMap.find( name, params ); + } + catch ( MethodMap.AmbiguousException ae ) + { + /* + * that's a miss :) + */ + + methodCache.put( methodKey, CACHE_MISS ); + + throw ae; + } + + if ( cacheEntry == null ) + { + methodCache.put( methodKey, CACHE_MISS ); + } + else + { + methodCache.put( methodKey, cacheEntry ); + } + } + + // Yes, this might just be null. + + return (Method) cacheEntry; + } + + /** + * Populate the Map of direct hits. These + * are taken from all the public methods + * that our class provides. + */ + private void populateMethodCache() + { + + /* + * get all publicly accessible methods + */ + + Method[] methods = getAccessibleMethods( clazz ); + + /* + * map and cache them + */ + + for ( Method method : methods ) + { + /* + * now get the 'public method', the method declared by a + * public interface or class. (because the actual implementing + * class may be a facade... + */ + + Method publicMethod = getPublicMethod( method ); + + /* + * it is entirely possible that there is no public method for + * the methods of this class (i.e. in the facade, a method + * that isn't on any of the interfaces or superclass + * in which case, ignore it. Otherwise, map and cache + */ + + if ( publicMethod != null ) + { + methodMap.add( publicMethod ); + methodCache.put( makeMethodKey( publicMethod ), publicMethod ); + } + } + } + + /** + * Make a methodKey for the given method using + * the concatenation of the name and the + * types of the method parameters. + */ + private String makeMethodKey( Method method ) + { + Class[] parameterTypes = method.getParameterTypes(); + + StringBuilder methodKey = new StringBuilder( method.getName() ); + + for ( Class parameterType : parameterTypes ) + { + /* + * If the argument type is primitive then we want + * to convert our primitive type signature to the + * corresponding Object type so introspection for + * methods with primitive types will work correctly. + */ + if ( parameterType.isPrimitive() ) + { + if ( parameterType.equals( Boolean.TYPE ) ) + { + methodKey.append( "java.lang.Boolean" ); + } + else if ( parameterType.equals( Byte.TYPE ) ) + { + methodKey.append( "java.lang.Byte" ); + } + else if ( parameterType.equals( Character.TYPE ) ) + { + methodKey.append( "java.lang.Character" ); + } + else if ( parameterType.equals( Double.TYPE ) ) + { + methodKey.append( "java.lang.Double" ); + } + else if ( parameterType.equals( Float.TYPE ) ) + { + methodKey.append( "java.lang.Float" ); + } + else if ( parameterType.equals( Integer.TYPE ) ) + { + methodKey.append( "java.lang.Integer" ); + } + else if ( parameterType.equals( Long.TYPE ) ) + { + methodKey.append( "java.lang.Long" ); + } + else if ( parameterType.equals( Short.TYPE ) ) + { + methodKey.append( "java.lang.Short" ); + } + } + else + { + methodKey.append( parameterType.getName() ); + } + } + + return methodKey.toString(); + } + + private static String makeMethodKey( String method, Object... params ) + { + StringBuilder methodKey = new StringBuilder().append( method ); + + for ( Object param : params ) + { + Object arg = param; + + if ( arg == null ) + { + arg = OBJECT; + } + + methodKey.append( arg.getClass().getName() ); + } + + return methodKey.toString(); + } + + /** + * Retrieves public methods for a class. In case the class is not + * public, retrieves methods with same signature as its public methods + * from public superclasses and interfaces (if they exist). Basically + * upcasts every method to the nearest acccessible method. + */ + private static Method[] getAccessibleMethods( Class clazz ) + { + Method[] methods = clazz.getMethods(); + + /* + * Short circuit for the (hopefully) majority of cases where the + * clazz is public + */ + + if ( Modifier.isPublic( clazz.getModifiers() ) ) + { + return methods; + } + + /* + * No luck - the class is not public, so we're going the longer way. + */ + + MethodInfo[] methodInfos = new MethodInfo[methods.length]; + + for ( int i = methods.length; i-- > 0; ) + { + methodInfos[i] = new MethodInfo( methods[i] ); + } + + int upcastCount = getAccessibleMethods( clazz, methodInfos, 0 ); + + /* + * Reallocate array in case some method had no accessible counterpart. + */ + + if ( upcastCount < methods.length ) + { + methods = new Method[upcastCount]; + } + + int j = 0; + for ( MethodInfo methodInfo : methodInfos ) + { + if ( methodInfo.upcast ) + { + methods[j++] = methodInfo.method; + } + } + return methods; + } + + /** + * Recursively finds a match for each method, starting with the class, and then + * searching the superclass and interfaces. + * + * @param clazz Class to check + * @param methodInfos array of methods we are searching to match + * @param upcastCount current number of methods we have matched + * @return count of matched methods + */ + private static int getAccessibleMethods( Class clazz, MethodInfo[] methodInfos, int upcastCount ) + { + int l = methodInfos.length; + + /* + * if this class is public, then check each of the currently + * 'non-upcasted' methods to see if we have a match + */ + + if ( Modifier.isPublic( clazz.getModifiers() ) ) + { + for ( int i = 0; i < l && upcastCount < l; ++i ) + { + try + { + MethodInfo methodInfo = methodInfos[i]; + + if ( !methodInfo.upcast ) + { + methodInfo.tryUpcasting( clazz ); + upcastCount++; + } + } + catch ( NoSuchMethodException e ) + { + /* + * Intentionally ignored - it means + * it wasn't found in the current class + */ + } + } + + /* + * Short circuit if all methods were upcast + */ + + if ( upcastCount == l ) + { + return upcastCount; + } + } + + /* + * Examine superclass + */ + + Class superclazz = clazz.getSuperclass(); + + if ( superclazz != null ) + { + upcastCount = getAccessibleMethods( superclazz, methodInfos, upcastCount ); + + /* + * Short circuit if all methods were upcast + */ + + if ( upcastCount == l ) + { + return upcastCount; + } + } + + /* + * Examine interfaces. Note we do it even if superclazz == null. + * This is redundant as currently java.lang.Object does not implement + * any interfaces, however nothing guarantees it will not in future. + */ + + Class[] interfaces = clazz.getInterfaces(); + + for ( int i = interfaces.length; i-- > 0; ) + { + upcastCount = getAccessibleMethods( interfaces[i], methodInfos, upcastCount ); + + /* + * Short circuit if all methods were upcast + */ + + if ( upcastCount == l ) + { + return upcastCount; + } + } + + return upcastCount; + } + + /** + * For a given method, retrieves its publicly accessible counterpart. + * This method will look for a method with same name + * and signature declared in a public superclass or implemented interface of this + * method's declaring class. This counterpart method is publicly callable. + * + * @param method a method whose publicly callable counterpart is requested. + * @return the publicly callable counterpart method. Note that if the parameter + * method is itself declared by a public class, this method is an identity + * function. + */ + private static Method getPublicMethod( Method method ) + { + Class clazz = method.getDeclaringClass(); + + /* + * Short circuit for (hopefully the majority of) cases where the declaring + * class is public. + */ + + if ( ( clazz.getModifiers() & Modifier.PUBLIC ) != 0 ) + { + return method; + } + + return getPublicMethod( clazz, method.getName(), method.getParameterTypes() ); + } + + /** + * Looks up the method with specified name and signature in the first public + * superclass or implemented interface of the class. + * + * @param clazz the class whose method is sought + * @param name the name of the method + * @param paramTypes the classes of method parameters + */ + private static Method getPublicMethod( Class clazz, String name, Class... paramTypes ) + { + /* + * if this class is public, then try to get it + */ + + if ( ( clazz.getModifiers() & Modifier.PUBLIC ) != 0 ) + { + try + { + return clazz.getMethod( name, paramTypes ); + } + catch ( NoSuchMethodException e ) + { + /* + * If the class does not have the method, then neither its + * superclass nor any of its interfaces has it so quickly return + * null. + */ + return null; + } + } + + /* + * try the superclass + */ + + Class superclazz = clazz.getSuperclass(); + + if ( superclazz != null ) + { + Method superclazzMethod = getPublicMethod( superclazz, name, paramTypes ); + + if ( superclazzMethod != null ) + { + return superclazzMethod; + } + } + + /* + * and interfaces + */ + + Class[] interfaces = clazz.getInterfaces(); + + for ( Class anInterface : interfaces ) + { + Method interfaceMethod = getPublicMethod( anInterface, name, paramTypes ); + + if ( interfaceMethod != null ) + { + return interfaceMethod; + } + } + + return null; + } + + /** + * Used for the iterative discovery process for public methods. + */ + private static final class MethodInfo + { + Method method; + + String name; + + Class[] parameterTypes; + + boolean upcast; + + MethodInfo( Method method ) + { + this.method = null; + name = method.getName(); + parameterTypes = method.getParameterTypes(); + upcast = false; + } + + void tryUpcasting( Class clazz ) + throws NoSuchMethodException + { + method = clazz.getMethod( name, parameterTypes ); + name = null; + parameterTypes = null; + upcast = true; + } + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/IntrospectionException.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/IntrospectionException.java new file mode 100644 index 000000000..924137c58 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/IntrospectionException.java @@ -0,0 +1,41 @@ +package org.apache.maven.shared.utils.introspection; + +/* + * 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. + */ + +class IntrospectionException + extends Exception +{ + + /** + * + */ + private static final long serialVersionUID = -6090771282553728784L; + + IntrospectionException( String message ) + { + super( message ); + } + + IntrospectionException( Throwable cause ) + { + super( cause ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/MethodMap.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/MethodMap.java new file mode 100644 index 000000000..8cf80f8d4 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/MethodMap.java @@ -0,0 +1,479 @@ +package org.apache.maven.shared.utils.introspection; + +/* + * 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. + */ + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * @author Jason van Zyl + * @author Bob McWhirter + * @author Christoph Reck + * @author Geir Magnusson Jr. + * @author Attila Szegedi + * + */ +class MethodMap +{ + private static final int MORE_SPECIFIC = 0; + + private static final int LESS_SPECIFIC = 1; + + private static final int INCOMPARABLE = 2; + + /** + * Keep track of all methods with the same name. + */ + private final Map> methodByNameMap = new Hashtable>(); + + /** + * Add a method to a list of methods by name. + * For a particular class we are keeping track + * of all the methods with the same name. + * + * @param method The method + */ + void add( Method method ) + { + String methodName = method.getName(); + + List l = get( methodName ); + + if ( l == null ) + { + l = new ArrayList(); + methodByNameMap.put( methodName, l ); + } + + l.add( method ); + } + + /** + * Return a list of methods with the same name. + * + * @param key The name of the method. + * @return List list of methods + */ + List get( String key ) + { + return methodByNameMap.get( key ); + } + + /** + *

+ * Find a method. Attempts to find the + * most specific applicable method using the + * algorithm described in the JLS section + * 15.12.2 (with the exception that it can't + * distinguish a primitive type argument from + * an object type argument, since in reflection + * primitive type arguments are represented by + * their object counterparts, so for an argument of + * type (say) java.lang.Integer, it will not be able + * to decide between a method that takes int and a + * method that takes java.lang.Integer as a parameter. + *

+ *

+ *

+ * This turns out to be a relatively rare case + * where this is needed - however, functionality + * like this is needed. + *

+ * + * @param methodName name of method + * @param args the actual arguments with which the method is called + * @return the most specific applicable method, or null if no + * method is applicable. + * @throws AmbiguousException if there is more than one maximally + * specific applicable method + */ + Method find( String methodName, Object... args ) + throws AmbiguousException + { + List methodList = get( methodName ); + + if ( methodList == null ) + { + return null; + } + + int l = args.length; + Class[] classes = new Class[l]; + + for ( int i = 0; i < l; ++i ) + { + Object arg = args[i]; + + /* + * if we are careful down below, a null argument goes in there + * so we can know that the null was passed to the method + */ + classes[i] = arg == null ? null : arg.getClass(); + } + + return getMostSpecific( methodList, classes ); + } + + /** + * simple distinguishable exception, used when + * we run across ambiguous overloading + */ + static class AmbiguousException + extends Exception + { + + private static final long serialVersionUID = 751688436639650618L; + } + + + private static Method getMostSpecific( List methods, Class... classes ) + throws AmbiguousException + { + LinkedList applicables = getApplicables( methods, classes ); + + if ( applicables.isEmpty() ) + { + return null; + } + + if ( applicables.size() == 1 ) + { + return applicables.getFirst(); + } + + /* + * This list will contain the maximally specific methods. Hopefully at + * the end of the below loop, the list will contain exactly one method, + * (the most specific method) otherwise we have ambiguity. + */ + + LinkedList maximals = new LinkedList(); + + for ( Method app : applicables ) + { + Class[] appArgs = app.getParameterTypes(); + boolean lessSpecific = false; + + for ( Iterator maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); ) + { + Method max = maximal.next(); + + switch ( moreSpecific( appArgs, max.getParameterTypes() ) ) + { + case MORE_SPECIFIC: + /* + * This method is more specific than the previously + * known maximally specific, so remove the old maximum. + */ + + maximal.remove(); + break; + + case LESS_SPECIFIC: + /* + * This method is less specific than some of the + * currently known maximally specific methods, so we + * won't add it into the set of maximally specific + * methods + */ + + lessSpecific = true; + break; + + default: + } + } + + if ( !lessSpecific ) + { + maximals.addLast( app ); + } + } + + if ( maximals.size() > 1 ) + { + // We have more than one maximally specific method + throw new AmbiguousException(); + } + + return maximals.getFirst(); + } + + /** + * Determines which method signature (represented by a class array) is more + * specific. This defines a partial ordering on the method signatures. + * + * @param c1 first signature to compare + * @param c2 second signature to compare + * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if + * c1 is less specific than c2, INCOMPARABLE if they are incomparable. + */ + private static int moreSpecific( Class[] c1, Class[] c2 ) + { + boolean c1MoreSpecific = false; + boolean c2MoreSpecific = false; + + for ( int i = 0; i < c1.length; ++i ) + { + if ( c1[i] != c2[i] ) + { + c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible( c2[i], c1[i] ); + c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible( c1[i], c2[i] ); + } + } + + if ( c1MoreSpecific ) + { + if ( c2MoreSpecific ) + { + /* + * Incomparable due to cross-assignable arguments (i.e. + * foo(String, Object) vs. foo(Object, String)) + */ + + return INCOMPARABLE; + } + + return MORE_SPECIFIC; + } + + if ( c2MoreSpecific ) + { + return LESS_SPECIFIC; + } + + /* + * Incomparable due to non-related arguments (i.e. + * foo(Runnable) vs. foo(Serializable)) + */ + + return INCOMPARABLE; + } + + /** + * Returns all methods that are applicable to actual argument types. + * + * @param methods list of all candidate methods + * @param classes the actual types of the arguments + * @return a list that contains only applicable methods (number of + * formal and actual arguments matches, and argument types are assignable + * to formal types through a method invocation conversion). + */ + private static LinkedList getApplicables( List methods, Class... classes ) + { + LinkedList list = new LinkedList(); + + for ( Method method : methods ) + { + if ( isApplicable( method, classes ) ) + { + list.add( method ); + } + } + return list; + } + + /** + * Returns true if the supplied method is applicable to actual + * argument types. + * + * @param method The method to check for applicability + * @param classes The arguments + * @return true if the method applies to the parameter types + */ + private static boolean isApplicable( Method method, Class... classes ) + { + Class[] methodArgs = method.getParameterTypes(); + + if ( methodArgs.length != classes.length ) + { + return false; + } + + for ( int i = 0; i < classes.length; ++i ) + { + if ( !isMethodInvocationConvertible( methodArgs[i], classes[i] ) ) + { + return false; + } + } + + return true; + } + + /** + * Determines whether a type represented by a class object is + * convertible to another type represented by a class object using a + * method invocation conversion, treating object types of primitive + * types as if they were primitive types (that is, a Boolean actual + * parameter type matches boolean primitive formal type). This behavior + * is because this method is used to determine applicable methods for + * an actual parameter list, and primitive types are represented by + * their object duals in reflective method calls. + * + * @param formal the formal parameter type to which the actual + * parameter type should be convertible + * @param actual the actual parameter type. + * @return true if either formal type is assignable from actual type, + * or formal is a primitive type and actual is its corresponding object + * type or an object type of a primitive type that can be converted to + * the formal type. + */ + private static boolean isMethodInvocationConvertible( Class formal, Class actual ) + { + /* + * if it's a null, it means the arg was null + */ + if ( actual == null && !formal.isPrimitive() ) + { + return true; + } + + /* + * Check for identity or widening reference conversion + */ + + if ( actual != null && formal.isAssignableFrom( actual ) ) + { + return true; + } + + /* + * Check for boxing with widening primitive conversion. Note that + * actual parameters are never primitives. + */ + + if ( formal.isPrimitive() ) + { + if ( formal == Boolean.TYPE && actual == Boolean.class ) + { + return true; + } + if ( formal == Character.TYPE && actual == Character.class ) + { + return true; + } + if ( formal == Byte.TYPE && actual == Byte.class ) + { + return true; + } + if ( formal == Short.TYPE && ( actual == Short.class || actual == Byte.class ) ) + { + return true; + } + if ( formal == Integer.TYPE + && ( actual == Integer.class || actual == Short.class || actual == Byte.class ) ) + { + return true; + } + if ( formal == Long.TYPE + && ( actual == Long.class || actual == Integer.class || actual == Short.class + || actual == Byte.class ) ) + { + return true; + } + if ( formal == Float.TYPE + && ( actual == Float.class || actual == Long.class || actual == Integer.class + || actual == Short.class || actual == Byte.class ) ) + { + return true; + } + if ( formal == Double.TYPE + && ( actual == Double.class || actual == Float.class || actual == Long.class || actual == Integer.class + || actual == Short.class || actual == Byte.class ) ) + { + return true; + } + } + + return false; + } + + /** + * Determines whether a type represented by a class object is + * convertible to another type represented by a class object using a + * method invocation conversion, without matching object and primitive + * types. This method is used to determine the more specific type when + * comparing signatures of methods. + * + * @param formal the formal parameter type to which the actual + * parameter type should be convertible + * @param actual the actual parameter type. + * @return true if either formal type is assignable from actual type, + * or formal and actual are both primitive types and actual can be + * subject to widening conversion to formal. + */ + private static boolean isStrictMethodInvocationConvertible( Class formal, Class actual ) + { + /* + * we shouldn't get a null into, but if so + */ + if ( actual == null && !formal.isPrimitive() ) + { + return true; + } + + /* + * Check for identity or widening reference conversion + */ + + if ( formal.isAssignableFrom( actual ) ) + { + return true; + } + + /* + * Check for widening primitive conversion. + */ + + if ( formal.isPrimitive() ) + { + if ( formal == Short.TYPE && ( actual == Byte.TYPE ) ) + { + return true; + } + if ( formal == Integer.TYPE && ( actual == Short.TYPE || actual == Byte.TYPE ) ) + { + return true; + } + if ( formal == Long.TYPE && ( actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE ) ) + { + return true; + } + if ( formal == Float.TYPE + && ( actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE ) ) + { + return true; + } + if ( formal == Double.TYPE + && ( actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE + || actual == Byte.TYPE ) ) + { + return true; + } + } + return false; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java new file mode 100644 index 000000000..963c98291 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractor.java @@ -0,0 +1,401 @@ +package org.apache.maven.shared.utils.introspection; + +/* + * 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. + */ + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.maven.shared.utils.StringUtils; +import org.apache.maven.shared.utils.introspection.MethodMap.AmbiguousException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + + +/** + *

Using simple dotted expressions to extract the values from an Object instance, + * For example we might want to extract a value like: project.build.sourceDirectory

+ *

+ *

The implementation supports indexed, nested and mapped properties similar to the JSP way.

+ * + * @author Jason van Zyl + * @author Vincent Siveton + * + * @see + * http://struts.apache.org/1.x/struts-taglib/indexedprops.html + */ +public class ReflectionValueExtractor +{ + private static final Class[] CLASS_ARGS = new Class[0]; + + private static final Object[] OBJECT_ARGS = new Object[0]; + + /** + * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. + * This approach prevents permgen space overflows due to retention of discarded + * classloaders. + */ + private static final Map, ClassMap> CLASS_MAPS = new WeakHashMap, ClassMap>(); + + static final int EOF = -1; + + static final char PROPERTY_START = '.'; + + static final char INDEXED_START = '['; + + static final char INDEXED_END = ']'; + + static final char MAPPED_START = '('; + + static final char MAPPED_END = ')'; + + static class Tokenizer + { + final String expression; + + int idx; + + Tokenizer( String expression ) + { + this.expression = expression; + } + + public int peekChar() + { + return idx < expression.length() ? expression.charAt( idx ) : EOF; + } + + public int skipChar() + { + return idx < expression.length() ? expression.charAt( idx++ ) : EOF; + } + + public String nextToken( char delimiter ) + { + int start = idx; + + while ( idx < expression.length() && delimiter != expression.charAt( idx ) ) + { + idx++; + } + + // delimiter MUST be present + if ( idx <= start || idx >= expression.length() ) + { + return null; + } + + return expression.substring( start, idx++ ); + } + + public String nextPropertyName() + { + final int start = idx; + + while ( idx < expression.length() && Character.isJavaIdentifierPart( expression.charAt( idx ) ) ) + { + idx++; + } + + // property name does not require delimiter + if ( idx <= start || idx > expression.length() ) + { + return null; + } + + return expression.substring( start, idx ); + } + + public int getPosition() + { + return idx < expression.length() ? idx : EOF; + } + + // to make tokenizer look pretty in debugger + @Override + public String toString() + { + return idx < expression.length() ? expression.substring( idx ) : ""; + } + } + + private ReflectionValueExtractor() + { + } + + /** + *

The implementation supports indexed, nested and mapped properties.

+ *

+ *

    + *
  • nested properties should be defined by a dot, i.e. "user.address.street"
  • + *
  • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] + * pattern, i.e. "user.addresses[1].street"
  • + *
  • mapped properties should be contains (\\w+)\\((.+)\\) pattern, + * i.e. "user.addresses(myAddress).street"
  • + *
      + * + * @param expression not null expression + * @param root not null object + * @return the object defined by the expression + * @throws IntrospectionException if any + */ + public static Object evaluate( @Nonnull String expression, @Nullable Object root ) + throws IntrospectionException + { + return evaluate( expression, root, true ); + } + + /** + *

      + * The implementation supports indexed, nested and mapped properties. + *

      + *

      + *

        + *
      • nested properties should be defined by a dot, i.e. "user.address.street"
      • + *
      • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] + * pattern, i.e. "user.addresses[1].street"
      • + *
      • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. + * "user.addresses(myAddress).street"
      • + *
          + * + * @param expression not null expression + * @param root not null object + * @param trimRootToken trim root token yes/no. + * @return the object defined by the expression + * @throws IntrospectionException if any + */ + public static Object evaluate( @Nonnull String expression, @Nullable Object root, boolean trimRootToken ) + throws IntrospectionException + { + Object value = root; + + // ---------------------------------------------------------------------- + // Walk the dots and retrieve the ultimate value desired from the + // MavenProject instance. + // ---------------------------------------------------------------------- + + if ( StringUtils.isEmpty( expression ) || !Character.isJavaIdentifierStart( expression.charAt( 0 ) ) ) + { + return null; + } + + boolean hasDots = expression.indexOf( PROPERTY_START ) >= 0; + + final Tokenizer tokenizer; + if ( trimRootToken && hasDots ) + { + tokenizer = new Tokenizer( expression ); + tokenizer.nextPropertyName(); + if ( tokenizer.getPosition() == EOF ) + { + return null; + } + } + else + { + tokenizer = new Tokenizer( "." + expression ); + } + + int propertyPosition = tokenizer.getPosition(); + while ( value != null && tokenizer.peekChar() != EOF ) + { + switch ( tokenizer.skipChar() ) + { + case INDEXED_START: + value = + getIndexedValue( expression, propertyPosition, tokenizer.getPosition(), value, + tokenizer.nextToken( INDEXED_END ) ); + break; + case MAPPED_START: + value = + getMappedValue( expression, propertyPosition, tokenizer.getPosition(), value, + tokenizer.nextToken( MAPPED_END ) ); + break; + case PROPERTY_START: + propertyPosition = tokenizer.getPosition(); + value = getPropertyValue( value, tokenizer.nextPropertyName() ); + break; + default: + // could not parse expression + return null; + } + } + + return value; + } + + private static Object getMappedValue( final String expression, final int from, final int to, final Object value, + final String key ) + throws IntrospectionException + { + if ( value == null || key == null ) + { + return null; + } + + if ( value instanceof Map ) + { + Object[] localParams = new Object[] { key }; + ClassMap classMap = getClassMap( value.getClass() ); + try + { + Method method = classMap.findMethod( "get", localParams ); + return method.invoke( value, localParams ); + } + catch ( AmbiguousException e ) + { + throw new IntrospectionException( e ); + } + catch ( IllegalAccessException e ) + { + throw new IntrospectionException( e ); + } + catch ( InvocationTargetException e ) + { + throw new IntrospectionException( e.getTargetException() ); + } + + } + + final String message = + String.format( "The token '%s' at position '%d' refers to a java.util.Map, but the value " + + "seems is an instance of '%s'", expression.subSequence( from, to ), from, value.getClass() ); + + throw new IntrospectionException( message ); + } + + private static Object getIndexedValue( final String expression, final int from, final int to, final Object value, + final String indexStr ) + throws IntrospectionException + { + try + { + int index = Integer.parseInt( indexStr ); + + if ( value.getClass().isArray() ) + { + return Array.get( value, index ); + } + + if ( value instanceof List ) + { + ClassMap classMap = getClassMap( value.getClass() ); + // use get method on List interface + Object[] localParams = new Object[] { index }; + Method method = null; + try + { + method = classMap.findMethod( "get", localParams ); + return method.invoke( value, localParams ); + } + catch ( AmbiguousException e ) + { + throw new IntrospectionException( e ); + } + catch ( IllegalAccessException e ) + { + throw new IntrospectionException( e ); + } + } + } + catch ( NumberFormatException e ) + { + return null; + } + catch ( InvocationTargetException e ) + { + // catch array index issues gracefully, otherwise release + if ( e.getCause() instanceof IndexOutOfBoundsException ) + { + return null; + } + + throw new IntrospectionException( e.getTargetException() ); + } + + final String message = + String.format( "The token '%s' at position '%d' refers to a java.util.List or an array, but the value " + + "seems is an instance of '%s'", expression.subSequence( from, to ), from, value.getClass() ); + + throw new IntrospectionException( message ); + } + + private static Object getPropertyValue( Object value, String property ) + throws IntrospectionException + { + if ( value == null || property == null ) + { + return null; + } + + ClassMap classMap = getClassMap( value.getClass() ); + String methodBase = StringUtils.capitalizeFirstLetter( property ); + String methodName = "get" + methodBase; + try + { + Method method = classMap.findMethod( methodName, CLASS_ARGS ); + + if ( method == null ) + { + // perhaps this is a boolean property?? + methodName = "is" + methodBase; + + method = classMap.findMethod( methodName, CLASS_ARGS ); + } + + if ( method == null ) + { + return null; + } + + return method.invoke( value, OBJECT_ARGS ); + } + catch ( InvocationTargetException e ) + { + throw new IntrospectionException( e.getTargetException() ); + } + catch ( AmbiguousException e ) + { + throw new IntrospectionException( e ); + } + catch ( IllegalAccessException e ) + { + throw new IntrospectionException( e ); + } + } + + private static ClassMap getClassMap( Class clazz ) + { + ClassMap classMap = CLASS_MAPS.get( clazz ); + + if ( classMap == null ) + { + classMap = new ClassMap( clazz ); + + CLASS_MAPS.put( clazz, classMap ); + } + + return classMap; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanResult.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanResult.java new file mode 100644 index 000000000..ae5de32bd --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanResult.java @@ -0,0 +1,62 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + + +/** + * Scan for files in a directory at a given time and reports removed and added files + * between captures. + * + * @deprecated use {@code java.nio.file.DirectoryStream} and related classes + */ +@Deprecated +public class DirectoryScanResult +{ + private final String[] filesAdded; + + private final String[] filesRemoved; + + /** + * @param filesAdded Added files. + * @param filesRemoved Removed files. + */ + public DirectoryScanResult( String[] filesAdded, String[] filesRemoved ) + { + this.filesAdded = filesAdded; + this.filesRemoved = filesRemoved; + } + + /** + * @return all files which got detected as being added between 2 capture calls + */ + public String[] getFilesAdded() + { + return filesAdded; + } + + /** + * @return all files which got detected as being removed between 2 capture calls + */ + public String[] getFilesRemoved() + { + return filesRemoved; + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java new file mode 100644 index 000000000..5d03525e7 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java @@ -0,0 +1,932 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Class for scanning a directory for files/directories which match certain criteria. + *

          + * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which + * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude + * files based on their filename. + *

          + * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is + * matched against a set of selectors, including special support for matching against filenames with include and and + * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file + * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will + * be placed in the list of files/directories found. + *

          + * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no + * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors + * are supplied, none are applied. + *

          + * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment + * is the name of a directory or file, which is bounded by File.separator ('/' under UNIX, '\' under + * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same + * is done for the pattern against which should be matched. + *

          + * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in + * the pattern, it matches zero or more path segments of the name. + *

          + * There is a special case regarding the use of File.separators at the beginning of the pattern and the + * string to match:
          + * When a pattern starts with a File.separator, the string to match must also start with a + * File.separator. When a pattern does not start with a File.separator, the string to match + * may not start with a File.separator. When one of these rules is not obeyed, the string will not match. + *

          + * When a name path segment is matched against a pattern path segment, the following special characters can be used:
          + * '*' matches zero or more characters
          + * '?' matches one character. + *

          + * Examples: + *

          + * "**\*.class" matches all .class files/dirs in a directory tree. + *

          + * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a + * directory called test. + *

          + * "**" matches everything in a directory tree. + *

          + * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test + * (e.g. "abc\test\def\ghi\XYZ123"). + *

          + * Case sensitivity may be turned off if necessary. By default, it is turned on. + *

          + * Example of usage: + *

          + *

          + * String[] includes = { "**\\*.class" };
          + * String[] excludes = { "modules\\*\\**" };
          + * ds.setIncludes( includes );
          + * ds.setExcludes( excludes );
          + * ds.setBasedir( new File( "test" ) );
          + * ds.setCaseSensitive( true );
          + * ds.scan();
          + *
          + * System.out.println( "FILES:" );
          + * String[] files = ds.getIncludedFiles();
          + * for ( int i = 0; i < files.length; i++ )
          + * {
          + *     System.out.println( files[i] );
          + * }
          + * 
          + *

          + * This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a + * directory called "modules" + *

          + * This class must not be used from multiple Threads concurrently! + * + * @author Arnout J. Kuiper ajkuiper@wxs.nl + * @author Magesh Umasankar + * @author Bruce Atherton + * @author Antoine Levy-Lambert + * @deprecated use {@code java.nio.file.DirectoryStream} and related classes + */ +@Deprecated +public class DirectoryScanner +{ + /** + * Patterns which should be excluded by default. + * + * @see #addDefaultExcludes() + */ + public static final String[] DEFAULTEXCLUDES = { + // Miscellaneous typical temporary files + "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", + + // CVS + "**/CVS", "**/CVS/**", "**/.cvsignore", + + // Subversion + "**/.svn", "**/.svn/**", + + // Arch + "**/.arch-ids", "**/.arch-ids/**", + + // Bazaar + "**/.bzr", "**/.bzr/**", + + // SurroundSCM + "**/.MySCMServerInfo", + + // Mac + "**/.DS_Store", + + // Serena Dimensions Version 10 + "**/.metadata", "**/.metadata/**", + + // Mercurial + "**/.hg", "**/.hg/**", + + // git + "**/.git", "**/.git/**", + + // BitKeeper + "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**", + + // darcs + "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" }; + + /** + * The base directory to be scanned. + */ + private File basedir; + + /** + * The patterns for the files to be included. + */ + private String[] includes; + + /** + * The patterns for the files to be excluded. + */ + private String[] excludes; + + private MatchPatterns excludesPatterns; + + private MatchPatterns includesPatterns; + + + /** + * The files which matched at least one include and no excludes and were selected. + */ + private List filesIncluded; + + /** + * The files which did not match any includes or selectors. + */ + private List filesNotIncluded; + + /** + * The files which matched at least one include and at least one exclude. + */ + private List filesExcluded; + + /** + * The directories which matched at least one include and no excludes and were selected. + */ + private List dirsIncluded; + + /** + * The directories which were found and did not match any includes. + */ + private List dirsNotIncluded; + + /** + * The directories which matched at least one include and at least one exclude. + */ + private List dirsExcluded; + + /** + * Whether or not our results were built by a slow scan. + */ + private boolean haveSlowResults = false; + + /** + * Whether or not the file system should be treated as a case sensitive one. + */ + private boolean isCaseSensitive = true; + + /** + * Whether or not symbolic links should be followed. + * + * + */ + private boolean followSymlinks = true; + + + /** + * A {@link ScanConductor} an control the scanning process. + */ + private ScanConductor scanConductor = null; + + /** + * The last ScanAction. We need to store this in the instance as the scan() method doesn't return + */ + private ScanConductor.ScanAction scanAction = null; + + /** + * Sole constructor. + */ + public DirectoryScanner() + { + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' + * characters are replaced by File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. Must not be null. + */ + public void setBasedir( final String basedir ) + { + setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) ); + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. + * + * @param basedir The base directory for scanning. Should not be null. + */ + public void setBasedir( @Nonnull final File basedir ) + { + this.basedir = basedir; + } + + /** + * Returns the base directory to be scanned. This is the directory which is scanned recursively. + * + * @return the base directory to be scanned + */ + public File getBasedir() + { + return basedir; + } + + /** + * Sets whether or not the file system should be regarded as case sensitive. + * + * @param isCaseSensitiveParameter whether or not the file system should be regarded as a case sensitive one + */ + public void setCaseSensitive( final boolean isCaseSensitiveParameter ) + { + this.isCaseSensitive = isCaseSensitiveParameter; + } + + /** + * Sets whether or not symbolic links should be followed. + * + * @param followSymlinks whether or not symbolic links should be followed + */ + public void setFollowSymlinks( final boolean followSymlinks ) + { + this.followSymlinks = followSymlinks; + } + + /** + * Sets the list of include patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param includes A list of include patterns. May be null, indicating that all files should be + * included. If a non-null list is given, all elements must be non-null. + */ + public void setIncludes( final String... includes ) + { + if ( includes == null ) + { + this.includes = null; + } + else + { + this.includes = new String[includes.length]; + for ( int i = 0; i < includes.length; i++ ) + { + String pattern; + pattern = includes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.includes[i] = pattern; + } + } + } + + /** + * Sets the list of exclude patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes A list of exclude patterns. May be null, indicating that no files should be + * excluded. If a non-null list is given, all elements must be non-null. + */ + public void setExcludes( final String... excludes ) + { + if ( excludes == null ) + { + this.excludes = null; + } + else + { + this.excludes = new String[excludes.length]; + for ( int i = 0; i < excludes.length; i++ ) + { + String pattern; + pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.excludes[i] = pattern; + } + } + } + + /** + * @param scanConductor {@link #scanConductor} + */ + public void setScanConductor( final ScanConductor scanConductor ) + { + this.scanConductor = scanConductor; + } + + /** + * Scans the base directory for files which match at least one include pattern and don't match any exclude patterns. + * If there are selectors then the files must pass muster there, as well. + * + * @throws IllegalStateException if the base directory was set incorrectly (i.e. if it is null, + * doesn't exist, or isn't a directory). + */ + public void scan() + throws IllegalStateException + { + if ( basedir == null ) + { + throw new IllegalStateException( "No basedir set" ); + } + if ( !basedir.exists() ) + { + throw new IllegalStateException( "basedir " + basedir + " does not exist" ); + } + if ( !basedir.isDirectory() ) + { + throw new IllegalStateException( "basedir " + basedir + " is not a directory" ); + } + + setupDefaultFilters(); + setupMatchPatterns(); + + filesIncluded = new ArrayList(); + filesNotIncluded = new ArrayList(); + filesExcluded = new ArrayList(); + dirsIncluded = new ArrayList(); + dirsNotIncluded = new ArrayList(); + dirsExcluded = new ArrayList(); + scanAction = ScanConductor.ScanAction.CONTINUE; + + if ( isIncluded( "" ) ) + { + if ( !isExcluded( "" ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( "", basedir ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) + || ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + return; + } + } + + dirsIncluded.add( "" ); + } + else + { + dirsExcluded.add( "" ); + } + } + else + { + dirsNotIncluded.add( "" ); + } + scandir( basedir, "", true ); + } + + /** + * Determine the file differences between the currently included files and + * a previously captured list of files. + * This method will not look for a changed in content but sole in the + * list of files given. + *

          + * The method will compare the given array of file Strings with the result + * of the last directory scan. It will execute a {@link #scan()} if no + * result of a previous scan could be found. + *

          + * The result of the diff can be queried by the methods + * {@link DirectoryScanResult#getFilesAdded()} and {@link DirectoryScanResult#getFilesRemoved()} + * + * @param oldFiles the list of previously captured files names. + * @return the result of the directory scan. + */ + public DirectoryScanResult diffIncludedFiles( String... oldFiles ) + { + if ( filesIncluded == null ) + { + // perform a scan if the directory didn't got scanned yet + scan(); + } + + return diffFiles( oldFiles, filesIncluded.toArray( new String[filesIncluded.size()] ) ); + } + + /** + * @param oldFiles array of old files + * @param newFiles array of new files + * @return calculated difference + */ + public static DirectoryScanResult diffFiles( @Nullable String[] oldFiles, @Nullable String[] newFiles ) + { + Set oldFileSet = arrayAsHashSet( oldFiles ); + Set newFileSet = arrayAsHashSet( newFiles ); + + List added = new ArrayList(); + List removed = new ArrayList(); + + for ( String oldFile : oldFileSet ) + { + if ( !newFileSet.contains( oldFile ) ) + { + removed.add( oldFile ); + } + } + + for ( String newFile : newFileSet ) + { + if ( !oldFileSet.contains( newFile ) ) + { + added.add( newFile ); + } + } + + String[] filesAdded = added.toArray( new String[added.size()] ); + String[] filesRemoved = removed.toArray( new String[removed.size()] ); + + return new DirectoryScanResult( filesAdded, filesRemoved ); + } + + + /** + * Take an array of type T and convert it into a HashSet of type T. + * If null or an empty array gets passed, an empty Set will be returned. + * + * @param array The array + * @return the filled HashSet of type T + */ + private static Set arrayAsHashSet( @Nullable T[] array ) + { + if ( array == null || array.length == 0 ) + { + return Collections.emptySet(); + } + + Set set = new HashSet( array.length ); + Collections.addAll( set, array ); + + return set; + } + + /** + * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories, + * whereas a fast scan will only have full results for included files, as it ignores directories which can't + * possibly hold any included files/directories. + *

          + * Returns immediately if a slow scan has already been completed. + */ + void slowScan() + { + if ( haveSlowResults ) + { + return; + } + + final String[] excl = dirsExcluded.toArray( new String[dirsExcluded.size()] ); + + final String[] notIncl = dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + + for ( String anExcl : excl ) + { + if ( !couldHoldIncluded( anExcl ) ) + { + scandir( new File( basedir, anExcl ), anExcl + File.separator, false ); + } + } + + for ( String aNotIncl : notIncl ) + { + if ( !couldHoldIncluded( aNotIncl ) ) + { + scandir( new File( basedir, aNotIncl ), aNotIncl + File.separator, false ); + } + } + + haveSlowResults = true; + } + + /** + * Scans the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast Whether or not this call is part of a fast scan. + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ + void scandir( @Nonnull final File dir, @Nonnull final String vpath, final boolean fast ) + { + String[] newfiles = dir.list(); + + if ( newfiles == null ) + { + /* + * two reasons are mentioned in the API docs for File.list (1) dir is not a directory. This is impossible as + * we wouldn't get here in this case. (2) an IO error occurred (why doesn't it throw an exception then???) + */ + + /* + * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... this is affecting the + * assembly plugin, fwiw. I will initialize the newfiles array as zero-length for now. NOTE: I can't find + * the problematic code, as it appears to come from a native method in UnixFileSystem... + */ + newfiles = new String[0]; + + // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() ); + } + + if ( !followSymlinks ) + { + newfiles = doNotFollowSymbolicLinks( dir, vpath, newfiles ); + } + + for ( final String newfile : newfiles ) + { + final String name = vpath + newfile; + final File file = new File( dir, newfile ); + if ( file.isDirectory() ) + { + if ( isIncluded( name ) ) + { + if ( !isExcluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( name, file ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + } + + if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + dirsIncluded.add( name ); + if ( fast ) + { + scandir( file, name + File.separator, fast ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + } + } + scanAction = null; + + } + else + { + dirsExcluded.add( name ); + if ( fast && couldHoldIncluded( name ) ) + { + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + scanAction = null; + } + } + } + else + { + if ( fast && couldHoldIncluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( name, file ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + } + if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + dirsNotIncluded.add( name ); + + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + } + scanAction = null; + } + } + if ( !fast ) + { + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + scanAction = null; + } + } + else if ( file.isFile() ) + { + if ( isIncluded( name ) ) + { + if ( !isExcluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitFile( name, file ); + } + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + + filesIncluded.add( name ); + } + else + { + filesExcluded.add( name ); + } + } + else + { + filesNotIncluded.add( name ); + } + } + } + } + + private String[] doNotFollowSymbolicLinks( final File dir, final String vpath, String[] newfiles ) + { + final List noLinks = new ArrayList(); + for ( final String newfile : newfiles ) + { + try + { + if ( isSymbolicLink( dir, newfile ) ) + { + final String name = vpath + newfile; + final File file = new File( dir, newfile ); + if ( file.isDirectory() ) + { + dirsExcluded.add( name ); + } + else + { + filesExcluded.add( name ); + } + } + else + { + noLinks.add( newfile ); + } + } + catch ( final IOException ioe ) + { + final String msg = + "IOException caught while checking " + "for links, couldn't get cannonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println( msg ); + noLinks.add( newfile ); + } + } + newfiles = noLinks.toArray( new String[noLinks.size()] ); + return newfiles; + } + + /** + * Tests whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + boolean isIncluded( final String name ) + { + return includesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches the start of at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + boolean couldHoldIncluded( @Nonnull final String name ) + { + return includesPatterns.matchesPatternStart( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + boolean isExcluded( @Nonnull final String name ) + { + return excludesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method does not work correctly on Windows. + * @return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to files. + */ + @Deprecated + public String[] getIncludedFiles() + { + if ( filesIncluded == null ) + { + return new String[0]; + } + return filesIncluded.toArray( new String[filesIncluded.size()] ); + } + + /** + * Returns the names of the files which matched none of the include patterns. The names are relative to the base + * directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the files which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedFiles() + { + slowScan(); + return filesNotIncluded.toArray( new String[filesNotIncluded.size()] ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and at least one of the exclude + * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not + * already been completed. + * + * @return the names of the files which matched at least one of the include patterns and at at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedFiles() + { + slowScan(); + return filesExcluded.toArray( new String[filesExcluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method is buggy. Do not depend on it. + * @return the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to directories. + */ + @Deprecated + public String[] getIncludedDirectories() + { + return dirsIncluded.toArray( new String[dirsIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched none of the include patterns. The names are relative to the + * base directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the directories which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedDirectories() + { + slowScan(); + return dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has + * not already been completed. + * + * @return the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedDirectories() + { + slowScan(); + return dirsExcluded.toArray( new String[dirsExcluded.size()] ); + } + + /** + * Adds default exclusions to the current exclusions set. + */ + public void addDefaultExcludes() + { + final int excludesLength = excludes == null ? 0 : excludes.length; + String[] newExcludes; + newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length]; + if ( excludesLength > 0 ) + { + System.arraycopy( excludes, 0, newExcludes, 0, excludesLength ); + } + for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ ) + { + newExcludes[i + excludesLength] = + DEFAULTEXCLUDES[i].replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + excludes = newExcludes; + } + + /** + * Checks whether a given file is a symbolic link. + *

          + * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are identical + * - this may lead to false positives on some platforms. + *

          + * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + */ + boolean isSymbolicLink( final File parent, final String name ) + throws IOException + { + return Files.isSymbolicLink( parent.toPath() ); + } + + private void setupDefaultFilters() + { + if ( includes == null ) + { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + if ( excludes == null ) + { + excludes = new String[0]; + } + } + + + private void setupMatchPatterns() + { + includesPatterns = MatchPatterns.from( includes ); + excludesPatterns = MatchPatterns.from( excludes ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryWalkListener.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryWalkListener.java new file mode 100644 index 000000000..bb142b5d0 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/DirectoryWalkListener.java @@ -0,0 +1,56 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; + +/** + * DirectoryWalkListener. + * + * @deprecated use {@code java.nio.file.FileVisitor} and related classes + */ +@Deprecated +public interface DirectoryWalkListener +{ + /** + * The directory walking has begun. + * + * @param basedir the basedir that walk started in + */ + void directoryWalkStarting( File basedir ); + + /** + * The included entry that was encountered. + * + * @param percentage rough percentage of the walk completed. (inaccurate) + * @param file the file that was included + */ + void directoryWalkStep( int percentage, File file ); + + /** + * The directory walking has finished. + */ + void directoryWalkFinished(); + + /** + * @param message the message for the debugging output + */ + void debug( String message ); +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/FileUtils.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/FileUtils.java new file mode 100644 index 000000000..a3be324c1 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/FileUtils.java @@ -0,0 +1,2146 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.WillClose; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * This class provides basic facilities for manipulating files and file paths. + *

          + *

          Path-related methods

          + *

          + *

          Methods exist to retrieve the components of a typical file path. For example + * /www/hosted/mysite/index.html, can be broken into: + *

            + *
          • /www/hosted/mysite/index -- retrievable through {@link #removeExtension}
          • + *
          • html -- retrievable through {@link #getExtension}
          • + *
          + *

          + *

          + *

          File-related methods

          + *

          + * There are methods to create a {@link #toFile File from a URL}, copy a + * copy a {@link #copyFile File to another File}, + * copy a {@link #copyURLToFile URL's contents to a File}, + * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File) + * clean} a directory. + *

          + *

          + * Common {@link java.io.File} manipulation routines. + *

          + * Taken from the commons-utils repo. + * Also code from Alexandria's FileUtils. + * And from Avalon Excalibur's IO. + * And from Ant. + * + * @author Kevin A. Burton + * @author Scott Sanders + * @author Daniel Rall + * @author Christoph.Reck + * @author Peter Donald + * @author Jeff Turner + * + */ +public class FileUtils +{ + /** + * protected constructor. + */ + protected FileUtils() + { + // This is a utility class. Normally don't instantiate + } + + /** + * The number of bytes in a kilobyte. + */ + private static final int ONE_KB = 1024; + + /** + * The number of bytes in a megabyte. + */ + private static final int ONE_MB = ONE_KB * ONE_KB; + + /** + * The file copy buffer size (30 MB) + */ + private static final int FILE_COPY_BUFFER_SIZE = ONE_MB * 30; + + /** + * The vm line separator + */ + private static final String FS = System.getProperty( "file.separator" ); + + /** + * Non-valid Characters for naming files, folders under Windows: ":", "*", "?", "\"", "<", ">", "|" + * + * @see + * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13 + */ + private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" }; + + /** + * @return the default excludes pattern + * @see DirectoryScanner#DEFAULTEXCLUDES + */ + @Nonnull public static String[] getDefaultExcludes() + { + return DirectoryScanner.DEFAULTEXCLUDES; + } + + /** + * @return the default excludes pattern as list + * @see #getDefaultExcludes() + */ + @Nonnull public static List getDefaultExcludesAsList() + { + return Arrays.asList( getDefaultExcludes() ); + } + + /** + * @return the default excludes pattern as comma separated string. + * @see DirectoryScanner#DEFAULTEXCLUDES + * @see StringUtils#join(Object[], String) + */ + @Nonnull public static String getDefaultExcludesAsString() + { + return StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, "," ); + } + + /** + * Returns the directory path portion of a file specification string. + * Matches the equally named unix command. + * + * @param path the file path + * @return the directory portion excluding the ending file separator + * @deprecated use {@code Paths.get(path).getParent().getName()} + */ + @Deprecated + @Nonnull public static String dirname( @Nonnull String path ) + { + int i = path.lastIndexOf( File.separator ); + return ( i >= 0 ? path.substring( 0, i ) : "" ); + } + + /** + * Returns the filename portion of a path. + * + * @param path the file path + * @return the filename string with extension + * @deprecated use {@code Paths.get(path).getName()} + */ + @Deprecated + @Nonnull public static String filename( @Nonnull String path ) + { + int i = path.lastIndexOf( File.separator ); + return ( i >= 0 ? path.substring( i + 1 ) : path ); + } + + /** + * Returns the extension portion of a file path. + * This is everything after the last dot '.' in the path (NOT including the dot). + * + * @param path the file path + * @return the extension of the file + * @deprecated use {@code org.apache.commons.io.FilenameUtils.getExtension} + */ + @Deprecated + @Nonnull public static String extension( @Nonnull String path ) + { + // Ensure the last dot is after the last file separator + int lastSep = path.lastIndexOf( File.separatorChar ); + int lastDot; + if ( lastSep < 0 ) + { + lastDot = path.lastIndexOf( '.' ); + } + else + { + lastDot = path.substring( lastSep + 1 ).lastIndexOf( '.' ); + if ( lastDot >= 0 ) + { + lastDot += lastSep + 1; + } + } + + if ( lastDot >= 0 && lastDot > lastSep ) + { + return path.substring( lastDot + 1 ); + } + + return ""; + } + + /** + * Check if a file exists. + * + * @param fileName the file path + * @return true if file exists + * @deprecated use {@code java.io.File.exists()} + */ + @Deprecated + public static boolean fileExists( @Nonnull String fileName ) + { + File file = new File( fileName ); + return file.exists(); + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file path + * @return the file content using the platform encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file))} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull String file ) + throws IOException + { + return fileRead( file, null ); + } + + /** + * @param file the file path + * @param encoding the wanted encoding + * @return the file content using the specified encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(Paths.get(file)), encoding)} + */ + @Deprecated + @Nonnull private static String fileRead( @Nonnull String file, @Nullable String encoding ) + throws IOException + { + return fileRead( new File( file ), encoding ); + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file path + * @return the file content using the platform encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file.toPath()))} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull File file ) + throws IOException + { + return fileRead( file, null ); + } + + /** + * @param file the file path + * @param encoding the wanted encoding + * @return the file content using the specified encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file.toPath()), encoding)} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull File file, @Nullable String encoding ) + throws IOException + { + Charset charset = charset( encoding ); + + StringBuilder buf = new StringBuilder(); + + + try ( Reader reader = Files.newBufferedReader( file.toPath(), charset ) ) + { + int count; + char[] b = new char[512]; + while ( ( count = reader.read( b ) ) >= 0 ) // blocking read + { + buf.append( b, 0, count ); + } + } + + return buf.toString(); + } + + /** + * @param file the file path + * @return the file content lines as String[] using the system default encoding. + * An empty List if the file doesn't exist. + * @throws IOException in case of failure + * @deprecated use {@code java.nio.files.Files.readAllLines()} + */ + @Deprecated + @Nonnull public static String[] fileReadArray( @Nonnull File file ) + throws IOException + { + List lines = loadFile( file ); + + return lines.toArray( new String[lines.size()] ); + } + + /** + * Appends data to a file. The file is created if it does not exist. + * Note: the data is written with platform encoding. + * + * @param fileName the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(), + * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileAppend( @Nonnull String fileName, @Nonnull String data ) + throws IOException + { + fileAppend( fileName, null, data ); + } + + /** + * Appends data to a file. The file will be created if it does not exist. + * + * @param fileName the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(encoding), + * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileAppend( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( OutputStream out = new FileOutputStream( fileName, true ) ) + { + out.write( data.getBytes( charset ) ); + } + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * Note: the data is written with platform encoding + * + * @param fileName the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, + * data.getBytes(), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull String fileName, @Nonnull String data ) + throws IOException + { + fileWrite( fileName, null, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param fileName the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(Paths.get(filename), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + File file = new File( fileName ); + fileWrite( file, encoding, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param file the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull File file, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) ) + { + writer.write( data ); + } + } + + /** + * Writes String array data to a file in the systems default encoding. + * The file will be created if it does not exist. + * + * @param file the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWriteArray( @Nonnull File file, @Nullable String... data ) + throws IOException + { + fileWriteArray( file, null, data ); + } + + /** + * Writes String array data to a file. The file is created if it does not exist. + * + * @param file the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWriteArray( @Nonnull File file, @Nullable String encoding, @Nullable String... data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) ) + { + for ( int i = 0; data != null && i < data.length; i++ ) + { + writer.write( data[i] ); + if ( i < data.length ) + { + writer.write( "\n" ); + } + } + } + } + + /** + * Deletes a file. + * + * @param fileName the path of the file to delete + * @deprecated use {@code Files.delete(Paths.get(fileName))} + */ + @Deprecated + public static void fileDelete( @Nonnull String fileName ) + { + File file = new File( fileName ); + //noinspection ResultOfMethodCallIgnored + deleteLegacyStyle( file ); + } + + /** + * Given a directory and an array of extensions return an array of compliant files. + *

          + * The given extensions should be like "java" and not like ".java". + * + * @param directory the path of the directory + * @param extensions an array of expected extensions + * @return an array of files for the wanted extensions + */ + public static String[] getFilesFromExtension( @Nonnull String directory, @Nonnull String... extensions ) + { + List files = new ArrayList(); + + File currentDir = new File( directory ); + + String[] unknownFiles = currentDir.list(); + + if ( unknownFiles == null ) + { + return new String[0]; + } + + for ( String unknownFile : unknownFiles ) + { + String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFile; + File currentFile = new File( currentFileName ); + + if ( currentFile.isDirectory() ) + { + // ignore all CVS directories... + if ( currentFile.getName().equals( "CVS" ) ) + { + continue; + } + + // ok... traverse into this directory and get all the files... then combine + // them with the current list. + + String[] fetchFiles = getFilesFromExtension( currentFileName, extensions ); + files = blendFilesToList( files, fetchFiles ); + } + else + { + // ok... add the file + + String add = currentFile.getAbsolutePath(); + if ( isValidFile( add, extensions ) ) + { + files.add( add ); + } + } + } + + // ok... move the Vector into the files list... + String[] foundFiles = new String[files.size()]; + files.toArray( foundFiles ); + + return foundFiles; + } + + /** + * Private helper method for getFilesFromExtension() + */ + @Nonnull private static List blendFilesToList( @Nonnull List v, @Nonnull String... files ) + { + Collections.addAll( v, files ); + + return v; + } + + /** + * Checks to see if a file is of a particular type(s). + * Note that if the file does not have an extension, an empty string + * ("") is matched for. + */ + private static boolean isValidFile( @Nonnull String file, @Nonnull String... extensions ) + { + String extension = extension( file ); + + // ok.. now that we have the "extension" go through the current know + // excepted extensions and determine if this one is OK. + + for ( String extension1 : extensions ) + { + if ( extension1.equals( extension ) ) + { + return true; + } + } + + return false; + + } + + /** + * Simple way to make a directory. + * + * @param dir the directory to create + * @throws IllegalArgumentException if the dir contains illegal Windows characters under Windows OS + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + * @deprecated use {@code java.nio.file.Files.createDirectories(Paths.get(dir))} + */ + @Deprecated + public static void mkdir( @Nonnull String dir ) + { + File file = new File( dir ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) + { + throw new IllegalArgumentException( + "The file (" + dir + ") cannot contain any of the following characters: \n" + StringUtils.join( + INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + } + + if ( !file.exists() ) + { + //noinspection ResultOfMethodCallIgnored + file.mkdirs(); + } + } + + /** + * Compare the contents of two files to determine if they are equal or not. + * + * @param file1 the first file + * @param file2 the second file + * @return true if the content of the files are equal or they both don't exist, false otherwise + * @throws IOException if any + */ + public static boolean contentEquals( @Nonnull final File file1, @Nonnull final File file2 ) + throws IOException + { + final boolean file1Exists = file1.exists(); + if ( file1Exists != file2.exists() ) + { + return false; + } + + if ( !file1Exists ) + { + // two not existing files are equal + return true; + } + + if ( file1.isDirectory() || file2.isDirectory() ) + { + // don't want to compare directory contents + return false; + } + + try ( InputStream input1 = new FileInputStream( file1 ); + InputStream input2 = new FileInputStream( file2 ) ) + { + return IOUtil.contentEquals( input1, input2 ); + } + } + + /** + * Convert from a URL to a File. + * + * @param url file URL + * @return the equivalent File object, or null if the URL's protocol + * is not file + */ + @Nullable public static File toFile( @Nullable final URL url ) + { + if ( url == null || !url.getProtocol().equalsIgnoreCase( "file" ) ) + { + return null; + } + + String filename = url.getFile().replace( '/', File.separatorChar ); + int pos = -1; + while ( ( pos = filename.indexOf( '%', pos + 1 ) ) >= 0 ) + { + if ( pos + 2 < filename.length() ) + { + String hexStr = filename.substring( pos + 1, pos + 3 ); + char ch = (char) Integer.parseInt( hexStr, 16 ); + filename = filename.substring( 0, pos ) + ch + filename.substring( pos + 3 ); + } + } + return new File( filename ); + } + + /** + * Convert the array of Files into a list of URLs. + * + * @param files the array of files + * @return the array of URLs + * @throws IOException if an error occurs + */ + @Nonnull public static URL[] toURLs( @Nonnull final File... files ) + throws IOException + { + final URL[] urls = new URL[files.length]; + + for ( int i = 0; i < urls.length; i++ ) + { + urls[i] = files[i].toURI().toURL(); + } + + return urls; + } + + /** + * Remove extension from a path. E.g. + *

          +     * foo.txt    --> foo
          +     * a\b\c.jpg --> a\b\c
          +     * a\b\c     --> a\b\c
          +     * 
          + * + * @param filename the path of the file + * @return the filename minus extension + * @deprecated use {@code org.apache.commons.io.FilenameUtils.removeExtension()} + */ + @Deprecated + @Nonnull public static String removeExtension( @Nonnull final String filename ) + { + String ext = extension( filename ); + + if ( "".equals( ext ) ) + { + return filename; + } + + final int index = filename.lastIndexOf( ext ) - 1; + return filename.substring( 0, index ); + } + + /** + * Get extension from a path. E.g. + * + *
          +     * foo.txt    --> "txt"
          +     * a\b\c.jpg --> "jpg"
          +     * a\b\c     --> ""
          +     * 
          + * + * @param filename the path of the file + * @return the extension of filename or "" if none + * @deprecated use {@code org.apache.commons.io.FilenameUtils.getExtension()} + */ + @Deprecated + @Nonnull public static String getExtension( @Nonnull final String filename ) + { + return extension( filename ); + } + + /** + * Copy file from source to destination. If destinationDirectory does not exist, it + * (and any parent directories) will be created. If a file source in + * destinationDirectory exists, it will be overwritten. + * + * @param source an existing File to copy + * @param destinationDirectory a directory to copy source into + * @throws java.io.FileNotFoundException if source isn't a normal file + * @throws IllegalArgumentException if destinationDirectory isn't a directory + * @throws IOException if source does not exist, the file in + * destinationDirectory cannot be written to, or an IO error + * occurs during copying. + * @deprecated use {@code org.apache.commons.io.FileUtils.copyFileToDirectory()} + */ + @Deprecated + public static void copyFileToDirectory( @Nonnull final File source, @Nonnull final File destinationDirectory ) + throws IOException + { + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFile( source, new File( destinationDirectory, source.getName() ) ); + } + + /** + * Copy file from source to destination only if source is newer than the target file. + * If destinationDirectory does not exist, it + * (and any parent directories) is created. If a file source in + * destinationDirectory exists, it is overwritten. + * + * @param source an existing File to copy + * @param destinationDirectory a directory to copy source into + * @throws java.io.FileNotFoundException if source isn't a normal file + * @throws IllegalArgumentException if destinationDirectory isn't a directory + * @throws IOException if source does not exist, the file in + * destinationDirectory cannot be written to, or an IO error + * occurs during copying + */ + private static void copyFileToDirectoryIfModified( @Nonnull final File source, + @Nonnull final File destinationDirectory ) + throws IOException + { + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFileIfModified( source, new File( destinationDirectory, source.getName() ) ); + } + + + /** + * Copy file from source to destination. The directories up to destination will be + * created if they don't already exist. destination will be overwritten if it + * already exists. + * + * @param source an existing non-directory File to copy bytes from + * @param destination a non-directory File to write bytes to (possibly + * overwriting) + * @throws IOException if source does not exist, destination cannot be + * written to, or an IO error occurs during copying + * @throws java.io.FileNotFoundException if destination is a directory + * @deprecated use {@code java.nio.Files.copy(source.toPath(), destination.toPath(), CopyOptions.NOFOLLOW_LINKS, + * CopyOptions.REPLACE_EXISTING)} + */ + @Deprecated + public static void copyFile( @Nonnull final File source, @Nonnull final File destination ) + throws IOException + { + //check source exists + if ( !source.exists() ) + { + final String message = "File " + source + " does not exist"; + throw new IOException( message ); + } + if ( Files.isSymbolicLink( source.toPath() ) ) + { + File target = Files.readSymbolicLink( source.toPath() ).toFile(); + createSymbolicLink( destination, target ); + return; + } + + //check source != destination, see PLXUTILS-10 + if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) ) + { + //if they are equal, we can exit the method without doing any work + return; + } + + mkdirsFor( destination ); + + doCopyFile( source, destination ); + + if ( source.length() != destination.length() ) + { + final String message = "Failed to copy full contents from " + source + " to " + destination; + throw new IOException( message ); + } + } + + private static void mkdirsFor( @Nonnull File destination ) + { + //does destination directory exist ? + if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) + { + //noinspection ResultOfMethodCallIgnored + destination.getParentFile().mkdirs(); + } + } + + private static void doCopyFile( @Nonnull File source, @Nonnull File destination ) + throws IOException + { + + try ( FileInputStream fis = new FileInputStream( source ); + FileOutputStream fos = new FileOutputStream( destination ); + FileChannel input = fis.getChannel(); + FileChannel output = fos.getChannel() ) + { + + long size = input.size(); + long pos = 0; + long count; + while ( pos < size ) + { + count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos; + pos += output.transferFrom( input, pos, count ); + } + } + + copyFilePermissions( source, destination ); + } + + /** + * Copy file from source to destination only if source timestamp is later than the destination timestamp. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source An existing non-directory File to copy bytes from. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @return true if no problem occured + * @throws IOException if source does not exist, destination cannot be + * written to, or an IO error occurs during copying. + */ + private static boolean copyFileIfModified( @Nonnull final File source, @Nonnull final File destination ) + throws IOException + { + if ( destination.lastModified() < source.lastModified() ) + { + copyFile( source, destination ); + + return true; + } + + return false; + } + + /** + * Copies bytes from the URL source to a file destination. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source A URL to copy bytes from. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @throws IOException if + *
            + *
          • source URL cannot be opened
          • + *
          • destination cannot be written to
          • + *
          • an IO error occurs during copying
          • + *
          + * @deprecated use {@code java.nio.Files.copy(source.openStream(), destination.toPath(), + * CopyOptions.REPLACE_EXISTING)} + */ + public static void copyURLToFile( @Nonnull final URL source, @Nonnull final File destination ) + throws IOException + { + copyStreamToFile( source.openStream(), destination ); + } + + /** + * Copies bytes from the {@link InputStream} source to a file destination. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source An {@link InputStream} to copy bytes from. This stream is + * guaranteed to be closed. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @throws IOException if + *
            + *
          • source cannot be opened
          • + *
          • destination cannot be written to
          • + *
          • an I/O error occurs during copying
          • + *
          + * @deprecated use {@code java.nio.Files.copy(source, destination.toPath(), + * CopyOptions.REPLACE_EXISTING)} + */ + @Deprecated + private static void copyStreamToFile( @Nonnull @WillClose final InputStream source, + @Nonnull final File destination ) + throws IOException + { + // does destination directory exist ? + if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) + { + // noinspection ResultOfMethodCallIgnored + destination.getParentFile().mkdirs(); + } + + // make sure we can write to destination + if ( destination.exists() && !destination.canWrite() ) + { + final String message = "Unable to open file " + destination + " for writing."; + throw new IOException( message ); + } + + try ( OutputStream out = new FileOutputStream( destination ); InputStream in = source ) + { + IOUtil.copy( in, out ); + } + } + + /** + * Normalize a path. + * Eliminates "/../" and "/./" in a string. Returns null if the ..'s went past the + * root. + * Eg: + *
          +     * /foo//               -->     /foo/
          +     * /foo/./              -->     /foo/
          +     * /foo/../bar          -->     /bar
          +     * /foo/../bar/         -->     /bar/
          +     * /foo/../bar/../baz   -->     /baz
          +     * //foo//./bar         -->     /foo/bar
          +     * /../                 -->     null
          +     * 
          + * + * @param path the path to normalize + * @return the normalized String, or null if too many ..'s. + * @deprecated use {@code org.apache.commons.io.FileNameUtils.normalize()} + */ + @Deprecated + @Nonnull public static String normalize( @Nonnull final String path ) + { + String normalized = path; + // Resolve occurrences of "//" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "//" ); + if ( index < 0 ) + { + break; + } + normalized = normalized.substring( 0, index ) + normalized.substring( index + 1 ); + } + + // Resolve occurrences of "/./" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "/./" ); + if ( index < 0 ) + { + break; + } + normalized = normalized.substring( 0, index ) + normalized.substring( index + 2 ); + } + + // Resolve occurrences of "/../" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "/../" ); + if ( index < 0 ) + { + break; + } + if ( index == 0 ) + { + return null; // Trying to go outside our context + } + int index2 = normalized.lastIndexOf( '/', index - 1 ); + normalized = normalized.substring( 0, index2 ) + normalized.substring( index + 3 ); + } + + // Return the normalized path that we have completed + return normalized; + } + + /** + * Resolve a file filename to it's canonical form. If filename is + * relative (doesn't start with /), it will be resolved relative to + * baseFile, otherwise it is treated as a normal root-relative path. + * + * @param baseFile Where to resolve filename from, if filename is + * relative. + * @param filename absolute or relative file path to resolve + * @return the canonical File of filename + */ + @Nonnull public static File resolveFile( final File baseFile, @Nonnull String filename ) + { + String filenm = filename; + if ( '/' != File.separatorChar ) + { + filenm = filename.replace( '/', File.separatorChar ); + } + + if ( '\\' != File.separatorChar ) + { + filenm = filename.replace( '\\', File.separatorChar ); + } + + // deal with absolute files + if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) ) + { + File file = new File( filenm ); + + try + { + file = file.getCanonicalFile(); + } + catch ( final IOException ioe ) + { + // nop + } + + return file; + } + // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips + // them. However, I'm not sure about this UNC stuff. (JT) + final char[] chars = filename.toCharArray(); + final StringBuilder sb = new StringBuilder(); + + //remove duplicate file separators in succession - except + //on win32 at start of filename as UNC filenames can + //be \\AComputer\AShare\myfile.txt + int start = 0; + if ( '\\' == File.separatorChar ) + { + sb.append( filenm.charAt( 0 ) ); + start++; + } + + for ( int i = start; i < chars.length; i++ ) + { + final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1]; + + if ( !doubleSeparator ) + { + sb.append( chars[i] ); + } + } + + filenm = sb.toString(); + + //must be relative + File file = ( new File( baseFile, filenm ) ).getAbsoluteFile(); + + try + { + file = file.getCanonicalFile(); + } + catch ( final IOException ioe ) + { + // nop + } + + return file; + } + + /** + * Delete a file. If file is directory, delete it and all sub-directories. + * + * @param file the file path + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()} + */ + @Deprecated + public static void forceDelete( @Nonnull final String file ) + throws IOException + { + forceDelete( new File( file ) ); + } + + /** + * Delete a file. If file is directory, delete it and all sub-directories. + * + * @param file a file + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()} + */ + @Deprecated + public static void forceDelete( @Nonnull final File file ) + throws IOException + { + if ( file.isDirectory() ) + { + deleteDirectory( file ); + } + else + { + /* + * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a + * symlink whose target does not exist is deleted, too. + */ + boolean filePresent = file.getCanonicalFile().exists(); + if ( !deleteFile( file ) && filePresent ) + { + final String message = "File " + file + " unable to be deleted."; + throw new IOException( message ); + } + } + } + + /** + * Deletes a file. + * + * @param file the file to delete + * @throws IOException if the file cannot be deleted + * @deprecated use {@code java.nio.files.Files.delete(file.toPath())} + */ + @Deprecated + public static void delete( @Nonnull File file ) + throws IOException + { + Files.delete( file.toPath() ); + } + + /** + * @param file the file + * @return true / false + * @deprecated use {@code java.nio.files.Files.delete(file.toPath())} + */ + @Deprecated + public static boolean deleteLegacyStyle( @Nonnull File file ) + { + try + { + Files.delete( file.toPath() ); + return true; + } + catch ( IOException e ) + { + return false; + } + } + + /** + * Accommodate Windows bug encountered in both Sun and IBM JDKs. + * Others possible. If the delete does not work, call System.gc(), + * wait a little and try again. + * + * @param file a file + * @throws IOException if any + */ + private static boolean deleteFile( @Nonnull File file ) + throws IOException + { + if ( file.isDirectory() ) + { + throw new IOException( "File " + file + " isn't a file." ); + } + + if ( !deleteLegacyStyle( file ) ) + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + file = file.getCanonicalFile(); + } + + try + { + Thread.sleep( 10 ); + return deleteLegacyStyle( file ); + } + catch ( InterruptedException ex ) + { + return deleteLegacyStyle( file ); + } + } + + return true; + } + + + /** + * Make a directory. + * + * @param file not null + * @throws IOException if a file already exists with the specified name or the directory is unable to be created + * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS. + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + */ + public static void forceMkdir( @Nonnull final File file ) + throws IOException + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) + { + throw new IllegalArgumentException( + "The file (" + file.getAbsolutePath() + ") cannot contain any of the following characters: \n" + + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + } + + if ( file.exists() ) + { + if ( file.isFile() ) + { + final String message = + "File " + file + " exists and is " + "not a directory. Unable to create directory."; + throw new IOException( message ); + } + } + else + { + if ( !file.mkdirs() ) + { + final String message = "Unable to create directory " + file; + throw new IOException( message ); + } + } + } + + /** + * Recursively delete a directory. + * + * @param directory a directory + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()} + */ + @Deprecated + public static void deleteDirectory( @Nonnull final String directory ) + throws IOException + { + deleteDirectory( new File( directory ) ); + } + + /** + * Recursively delete a directory. + * + * @param directory a directory + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()} + */ + @Deprecated + public static void deleteDirectory( @Nonnull final File directory ) + throws IOException + { + if ( !directory.exists() ) + { + return; + } + + /* try delete the directory before its contents, which will take + * care of any directories that are really symbolic links. + */ + if ( deleteLegacyStyle( directory ) ) + { + return; + } + + cleanDirectory( directory ); + if ( !deleteLegacyStyle( directory ) ) + { + final String message = "Directory " + directory + " unable to be deleted."; + throw new IOException( message ); + } + } + + /** + * Remove all files from a directory without deleting it. + * + * @param directory a directory + * @throws IOException if any. This can leave cleaning in a half-finished state where + * some but not all files have been deleted. + * @deprecated use {@code org.apache.commons.io.FileUtils.cleanDirectory()} + */ + @Deprecated + public static void cleanDirectory( @Nonnull final File directory ) + throws IOException + { + if ( !directory.exists() ) + { + final String message = directory + " does not exist"; + throw new IllegalArgumentException( message ); + } + + if ( !directory.isDirectory() ) + { + final String message = directory + " is not a directory"; + throw new IllegalArgumentException( message ); + } + + IOException exception = null; + + final File[] files = directory.listFiles(); + + if ( files == null ) + { + return; + } + + for ( final File file : files ) + { + try + { + forceDelete( file ); + } + catch ( final IOException ioe ) + { + exception = ioe; + } + } + + if ( null != exception ) + { + throw exception; + } + } + + /** + * Recursively count size of a directory. + * + * @param directory a directory + * @return size of directory in bytes + * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()} + */ + @Deprecated + public static long sizeOfDirectory( @Nonnull final String directory ) + { + return sizeOfDirectory( new File( directory ) ); + } + + /** + * Recursively count size of a directory. + * + * @param directory a directory + * @return size of directory in bytes + * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()} + */ + @Deprecated + public static long sizeOfDirectory( @Nonnull final File directory ) + { + if ( !directory.exists() ) + { + final String message = directory + " does not exist"; + throw new IllegalArgumentException( message ); + } + + if ( !directory.isDirectory() ) + { + final String message = directory + " is not a directory"; + throw new IllegalArgumentException( message ); + } + + long size = 0; + + final File[] files = directory.listFiles(); + if ( files == null ) + { + throw new IllegalArgumentException( "Problems reading directory" ); + } + + for ( final File file : files ) + { + if ( file.isDirectory() ) + { + size += sizeOfDirectory( file ); + } + else + { + size += file.length(); + } + } + + return size; + } + + /** + * Return the files contained in the directory, using inclusion and exclusion Ant patterns, + * including the directory name in each of the files + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @return a list of File objects + * @throws IOException in case of failure. + * @see #getFileNames(File, String, String, boolean) + */ + @Nonnull + public static List getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes ) + throws IOException + { + return getFiles( directory, includes, excludes, true ); + } + + /** + * Return the files contained in the directory, using inclusion and exclusion Ant patterns + * + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each file + * @return a list of File objects + * @throws IOException in case of failure. + * @see #getFileNames(File, String, String, boolean) + */ + @Nonnull + public static List getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, + boolean includeBasedir ) + throws IOException + { + List fileNames = getFileNames( directory, includes, excludes, includeBasedir ); + + List files = new ArrayList(); + + for ( String filename : fileNames ) + { + files.add( new File( filename ) ); + } + + return files; + } + + /** + * Return a list of files as String depending options. + * This method use case sensitive file name. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @return a list of file names + * @throws IOException in case of failure + */ + @Nonnull public static List getFileNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir ) + throws IOException + { + return getFileNames( directory, includes, excludes, includeBasedir, true ); + } + + /** + * Return a list of files as String depending options. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each String of file + * @param isCaseSensitive true if case sensitive + * @return a list of files as String + * @throws IOException + */ + @Nonnull private static List getFileNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive ) + throws IOException + { + return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false ); + } + + /** + * Return a list of directories as String depending options. + * This method use case sensitive file name. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each String of file + * @return a list of directories as String + * @throws IOException in case of failure. + */ + @Nonnull public static List getDirectoryNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir ) + throws IOException + { + return getDirectoryNames( directory, includes, excludes, includeBasedir, true ); + } + + /** + * Return a list of directories as Strings. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @param isCaseSensitive true if case sensitive + * @return a list of directories as String + * @throws IOException in case of failure + */ + @Nonnull public static List getDirectoryNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive ) + throws IOException + { + return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true ); + } + + /** + * Return a list of file names as Strings. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @param isCaseSensitive true if case sensitive + * @param getFiles true to include regular files + * @param getDirectories true to include directories + * @return a list of file names + */ + @Nonnull public static List getFileAndDirectoryNames( File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive, boolean getFiles, + boolean getDirectories ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + scanner.setBasedir( directory ); + + if ( includes != null ) + { + scanner.setIncludes( StringUtils.split( includes, "," ) ); + } + + if ( excludes != null ) + { + scanner.setExcludes( StringUtils.split( excludes, "," ) ); + } + + scanner.setCaseSensitive( isCaseSensitive ); + + scanner.scan(); + + List list = new ArrayList(); + + if ( getFiles ) + { + String[] files = scanner.getIncludedFiles(); + + for ( String file : files ) + { + if ( includeBasedir ) + { + list.add( directory + FileUtils.FS + file ); + } + else + { + list.add( file ); + } + } + } + + if ( getDirectories ) + { + String[] directories = scanner.getIncludedDirectories(); + + for ( String directory1 : directories ) + { + if ( includeBasedir ) + { + list.add( directory + FileUtils.FS + directory1 ); + } + else + { + list.add( directory1 ); + } + } + } + + return list; + } + + /** + * Copy the contents of a directory into another one. + * + * @param sourceDirectory the source directory + * @param destinationDirectory the target directory + * @throws IOException if any + */ + public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) + throws IOException + { + copyDirectory( sourceDirectory, destinationDirectory, "**", null ); + } + + /** + * Copy the contents of a directory into another one. + * + * @param sourceDirectory the source directory + * @param destinationDirectory the target directory + * @param includes Ant include pattern + * @param excludes Ant exclude pattern + * @throws IOException if any + * @see #getFiles(File, String, String) + */ + public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, + @Nullable String includes, @Nullable String excludes ) + throws IOException + { + if ( !sourceDirectory.exists() ) + { + return; + } + + List files = getFiles( sourceDirectory, includes, excludes ); + + for ( File file : files ) + { + copyFileToDirectory( file, destinationDirectory ); + } + } + + /** + * Copies an entire directory structure. + *

          + * Note: + *

            + *
          • It will include empty directories. + *
          • The sourceDirectory must exist. + *
          + * + * @param sourceDirectory the source dir + * @param destinationDirectory the target dir + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.copyDirectory()} + */ + @Deprecated + public static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) + throws IOException + { + copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, false ); + } + + private static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, + File rootDestinationDirectory, boolean onlyModifiedFiles ) + throws IOException + { + //noinspection ConstantConditions + if ( sourceDirectory == null ) + { + throw new IOException( "source directory can't be null." ); + } + + //noinspection ConstantConditions + if ( destinationDirectory == null ) + { + throw new IOException( "destination directory can't be null." ); + } + + if ( sourceDirectory.equals( destinationDirectory ) ) + { + throw new IOException( "source and destination are the same directory." ); + } + + if ( !sourceDirectory.exists() ) + { + throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); + } + + File[] files = sourceDirectory.listFiles(); + + if ( files == null ) + { + return; + } + + String sourcePath = sourceDirectory.getAbsolutePath(); + + for ( File file : files ) + { + if ( file.equals( rootDestinationDirectory ) ) + { + // We don't copy the destination directory in itself + continue; + } + + String dest = file.getAbsolutePath(); + + dest = dest.substring( sourcePath.length() + 1 ); + + File destination = new File( destinationDirectory, dest ); + + if ( file.isFile() ) + { + destination = destination.getParentFile(); + + if ( onlyModifiedFiles ) + { + copyFileToDirectoryIfModified( file, destination ); + } + else + { + copyFileToDirectory( file, destination ); + } + } + else if ( file.isDirectory() ) + { + if ( !destination.exists() && !destination.mkdirs() ) + { + throw new IOException( + "Could not create destination directory '" + destination.getAbsolutePath() + "'." ); + } + + copyDirectoryStructure( file, destination, rootDestinationDirectory, onlyModifiedFiles ); + } + else + { + throw new IOException( "Unknown file type: " + file.getAbsolutePath() ); + } + } + } + + /** + * Renames a file, even if that involves crossing file system boundaries. + *

          + *

          This will remove to (if it exists), ensure that + * to's parent directory exists and move + * from, which involves deleting from as + * well.

          + * + * @param from the file to move + * @param to the new file name + * @throws IOException if anything bad happens during this process. + * Note that to may have been deleted already when this happens. + * @deprecated use {@code java.nio.Files.move()} + */ + @Deprecated + public static void rename( @Nonnull File from, @Nonnull File to ) + throws IOException + { + if ( to.exists() && !deleteLegacyStyle( to ) ) + { + throw new IOException( "Failed to delete " + to + " while trying to rename " + from ); + } + + File parent = to.getParentFile(); + if ( parent != null && !parent.exists() && !parent.mkdirs() ) + { + throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from ); + } + + if ( !from.renameTo( to ) ) + { + copyFile( from, to ); + if ( !deleteLegacyStyle( from ) ) + { + throw new IOException( "Failed to delete " + from + " while trying to rename it." ); + } + } + } + + /** + * Create a temporary file in a given directory. + *

          + *

          The file denoted by the returned abstract pathname did not + * exist before this method was invoked, any subsequent invocation + * of this method will yield a different file name.

          + *

          + * The filename is prefixNNNNNsuffix where NNNN is a random number + *

          + *

          This method is different to {@link File#createTempFile(String, String, File)} + * as it doesn't create the file itself. + * It uses the location pointed to by java.io.tmpdir + * when the parentDir attribute is null.

          + *

          To automatically delete the file created by this method, use the + * {@link File#deleteOnExit()} method.

          + * + * @param prefix prefix before the random number + * @param suffix file extension; include the '.' + * @param parentDir directory to create the temporary file in -java.io.tmpdir + * used if not specified + * @return a File reference to the new temporary file. + * @deprecated use {@code java.nio.Files.createTempFile()} + */ + @Deprecated + public static File createTempFile( @Nonnull String prefix, @Nonnull String suffix, @Nullable File parentDir ) + { + File result; + String parent = System.getProperty( "java.io.tmpdir" ); + if ( parentDir != null ) + { + parent = parentDir.getPath(); + } + DecimalFormat fmt = new DecimalFormat( "#####" ); + SecureRandom secureRandom = new SecureRandom(); + long secureInitializer = secureRandom.nextLong(); + Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() ); + do + { + result = new File( parent, prefix + fmt.format( positiveRandom( rand ) ) + suffix ); + } + while ( result.exists() ); + + return result; + } + + private static int positiveRandom( Random rand ) + { + int a = rand.nextInt(); + while ( a == Integer.MIN_VALUE ) + { + a = rand.nextInt(); + } + return Math.abs( a ); + } + + /** + * If wrappers is null or empty, the file will be copied only if to.lastModified() < from.lastModified() + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} + * @throws IOException if an IO error occurs during copying or filtering + */ + public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, + @Nullable FilterWrapper... wrappers ) + throws IOException + { + copyFile( from, to, encoding, wrappers, false ); + } + + /** + * Wrapper class for Filter. + */ + public abstract static class FilterWrapper + { + /** + * @param fileReader {@link Reader} + * @return the Reader instance + */ + public abstract Reader getReader( Reader fileReader ); + } + + /** + * If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if + * overwrite is true + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} + * @param overwrite if true and wrappers is null or empty, the file will be copied even if + * to.lastModified() < from.lastModified() + * @throws IOException if an IO error occurs during copying or filtering + */ + public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, + @Nullable FilterWrapper[] wrappers, boolean overwrite ) + throws IOException + { + if ( wrappers == null || wrappers.length == 0 ) + { + if ( overwrite || to.lastModified() < from.lastModified() ) + { + copyFile( from, to ); + } + } + else + { + Charset charset = charset( encoding ); + + // buffer so it isn't reading a byte at a time! + try ( Reader fileReader = Files.newBufferedReader( from.toPath(), charset ) ) + { + Reader wrapped = fileReader; + for ( FilterWrapper wrapper : wrappers ) + { + wrapped = wrapper.getReader( wrapped ); + } + + if ( overwrite || !to.exists() ) + { + try ( Writer fileWriter = Files.newBufferedWriter( to.toPath(), charset ) ) + { + IOUtil.copy( wrapped, fileWriter ); + } + } + else + { + CharsetEncoder encoder = charset.newEncoder(); + + int totalBufferSize = FILE_COPY_BUFFER_SIZE; + + int charBufferSize = ( int ) Math.floor( totalBufferSize / ( 2 + 2 * encoder.maxBytesPerChar() ) ); + int byteBufferSize = ( int ) Math.ceil( charBufferSize * encoder.maxBytesPerChar() ); + + CharBuffer newChars = CharBuffer.allocate( charBufferSize ); + ByteBuffer newBytes = ByteBuffer.allocate( byteBufferSize ); + ByteBuffer existingBytes = ByteBuffer.allocate( byteBufferSize ); + + CoderResult coderResult; + int existingRead; + boolean writing = false; + + try ( final RandomAccessFile existing = new RandomAccessFile( to, "rw" ) ) + { + int n; + while ( -1 != ( n = wrapped.read( newChars ) ) ) + { + ( ( Buffer ) newChars ).flip(); + + coderResult = encoder.encode( newChars, newBytes, n != 0 ); + if ( coderResult.isError() ) + { + coderResult.throwException(); + } + + ( ( Buffer ) newBytes ).flip(); + + if ( !writing ) + { + existingRead = existing.read( existingBytes.array(), 0, newBytes.remaining() ); + ( ( Buffer ) existingBytes ).position( existingRead ); + ( ( Buffer ) existingBytes ).flip(); + + if ( newBytes.compareTo( existingBytes ) != 0 ) + { + writing = true; + if ( existingRead > 0 ) + { + existing.seek( existing.getFilePointer() - existingRead ); + } + } + } + + if ( writing ) + { + existing.write( newBytes.array(), 0, newBytes.remaining() ); + } + + ( ( Buffer ) newChars ).clear(); + ( ( Buffer ) newBytes ).clear(); + ( ( Buffer ) existingBytes ).clear(); + } + + if ( existing.length() > existing.getFilePointer() ) + { + existing.setLength( existing.getFilePointer() ); + } + } + } + } + } + + copyFilePermissions( from, to ); + } + + /** + * Attempts to copy file permissions from the source to the destination file. + * Initially attempts to copy posix file permissions, assuming that the files are both on posix filesystems. + * If the initial attempts fail then a second attempt using less precise permissions model. + * Note that permissions are copied on a best-efforts basis, + * failure to copy permissions will not result in an exception. + * + * @param source the file to copy permissions from. + * @param destination the file to copy permissions to. + */ + private static void copyFilePermissions( @Nonnull File source, @Nonnull File destination ) + throws IOException + { + try + { + // attempt to copy posix file permissions + Files.setPosixFilePermissions( + destination.toPath(), + Files.getPosixFilePermissions( source.toPath() ) + ); + } + catch ( UnsupportedOperationException e ) + { + // fallback to setting partial permissions + destination.setExecutable( source.canExecute() ); + destination.setReadable( source.canRead() ); + destination.setWritable( source.canWrite() ); + } + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file + * @return a List containing every every line not starting with # and not empty + * @throws IOException if any + * @deprecated assumes the platform default character set + */ + @Deprecated + @Nonnull public static List loadFile( @Nonnull File file ) + throws IOException + { + List lines = new ArrayList(); + + if ( file.exists() ) + { + try ( BufferedReader reader = Files.newBufferedReader( file.toPath(), Charset.defaultCharset() ) ) + { + for ( String line = reader.readLine(); line != null; line = reader.readLine() ) + { + line = line.trim(); + if ( !line.startsWith( "#" ) && line.length() != 0 ) + { + lines.add( line ); + } + } + } + } + + return lines; + + } + + /** + * Returns the named charset or the default charset. + * @param encoding the name or alias of the charset, null or empty + * @return A charset object for the named or default charset. + */ + private static Charset charset( String encoding ) + { + if ( encoding == null || encoding.isEmpty() ) + { + return Charset.defaultCharset(); + } + else + { + return Charset.forName( encoding ); + } + } + + /** + * For Windows OS, check if the file name contains any of the following characters: + * ":", "*", "?", "\"", "<", ">", "|" + * + * @param f not null file + * @return false if the file path contains any of forbidden Windows characters, + * true if the Os is not Windows or if the file path respect the Windows constraints. + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + */ + private static boolean isValidWindowsFileName( @Nonnull File f ) + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 ) + { + return false; + } + + if ( f.getParentFile() != null ) + { + return isValidWindowsFileName( f.getParentFile() ); + } + } + + return true; + } + + /** + * Checks whether a given file is a symbolic link. + * + * @param file the file to check + * @throws IOException in case of failure. + * @return true if symbolic link false otherwise. + * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())} + */ + @Deprecated + public static boolean isSymbolicLink( @Nonnull final File file ) + throws IOException + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * Checks whether a given file is a symbolic link. + * + * @param file the file to check + * @return true if and only if we reliably can say this is a symlink + * + * @throws IOException in case of failure + * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())} + */ + @Deprecated + public static boolean isSymbolicLinkForSure( @Nonnull final File file ) + throws IOException + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * Create a new symbolic link, possibly replacing an existing symbolic link. + * + * @param symlink the link name + * @param target the target + * @return the linked file + * @throws IOException in case of an error + * @see {@code java.nio.file.Files.createSymbolicLink(Path)} which creates a new + * symbolic link but does not replace exsiting symbolic links + */ + @Nonnull public static File createSymbolicLink( @Nonnull File symlink, @Nonnull File target ) + throws IOException + { + final Path symlinkPath = symlink.toPath(); + + if ( Files.exists( symlinkPath ) ) + { + if ( target.equals( Files.readSymbolicLink( symlinkPath ).toFile() ) ) + { + return symlink; + } + + Files.delete( symlinkPath ); + } + + return Files.createSymbolicLink( symlinkPath, target.toPath() ).toFile(); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/IOUtil.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/IOUtil.java new file mode 100644 index 000000000..11f1bdd4e --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/IOUtil.java @@ -0,0 +1,1368 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.channels.Channel; + +/** + * General IO Stream manipulation. + *

          + * This class provides static utility methods for input/output operations, particularly buffered + * copying between sources (InputStream, Reader, String and + * byte[]) and destinations (OutputStream, Writer, + * String and byte[]). + * + *

          Unless otherwise noted, these copy methods do not flush or close the + * streams. Often, doing so would require making non-portable assumptions about the streams' origin + * and further use. This means that both streams' close() methods must be called after + * copying. if one omits this step, then the stream resources (sockets, file descriptors) are + * released when the associated Stream is garbage-collected. It is not a good idea to rely on this + * mechanism.

          + * + *

          For each copy method, a variant is provided that allows the caller to specify the + * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this + * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data + * transfers.

          + *

          For byte-to-char methods, a copy variant allows the encoding to be selected + * (otherwise the platform default is used).

          + *

          The copy methods use an internal buffer when copying. It is therefore advisable + * not to deliberately wrap the stream arguments to the copy methods in + * Buffered* streams. For example, don't do the + * following:

          + *

          + * copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) ); + *

          + *

          The rationale is as follows:

          + * + *

          Imagine that an InputStream's read() is a very expensive operation, which would usually suggest + * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent + * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to + * fill an internal buffer, from which further read requests can inexpensively get + * their data (until the buffer runs out).

          + *

          However, the copy methods do the same thing, keeping an internal buffer, + * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers + * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer + * management hurts performance slightly (about 3%, according to some simple experiments).

          + * + * @author Peter Donald + * @author Jeff Turner + * @version CVS $Revision$ $Date$ + * + */ +public final class IOUtil +/* + * Behold, intrepid explorers; a map of this class: + * + * Method Input Output Dependency + * ------ ----- ------ ------- + * 1 copy InputStream OutputStream (primitive) + * 2 copy Reader Writer (primitive) + * + * 3 copy InputStream Writer 2 + * 4 toString InputStream String 3 + * 5 toByteArray InputStream byte[] 1 + * + * 6 copy Reader OutputStream 2 + * 7 toString Reader String 2 + * 8 toByteArray Reader byte[] 6 + * + * 9 copy String OutputStream 2 + * 10 copy String Writer (trivial) + * 11 toByteArray String byte[] 9 + * + * 12 copy byte[] Writer 3 + * 13 toString byte[] String 12 + * 14 copy byte[] OutputStream (trivial) + * + * + * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy + * using native Java copy methods. As there are method variants to specify buffer size and encoding, + * each row may correspond to up to 4 methods. + * + */ +{ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Private constructor to prevent instantiation. + */ + private IOUtil() + { + } + + /////////////////////////////////////////////////////////////// + // Core copy methods + /////////////////////////////////////////////////////////////// + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * @param input the stream to read from + * @param output the stream to write to + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * In Java 9 and later this is replaced by {@code InputStream.transferTo()}. + * + * @param input the stream to read from + * @param output the stream to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output, + final int bufferSize ) + throws IOException + { + final byte[] buffer = new byte[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final char[] buffer = new char[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + output.flush(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // InputStream -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // InputStream -> Writer + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> String + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @return the converted string + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> byte[] + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // Reader -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // Reader -> OutputStream + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output stream to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( input, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> String + + /** + * Get the contents of a Reader as a String. + * @param input the InputStream to read from + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a String. + * + * @param input the reader to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> byte[] + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // String -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // String -> OutputStream + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final StringReader in = new StringReader( input ); + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( in, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // String -> Writer + + /** + * Copy chars from a String to a Writer. + * + * @param input the string to write + * @param output resulting output {@link Writer} + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final Writer output ) + throws IOException + { + output.write( input ); + } + + /////////////////////////////////////////////////////////////// + // String -> byte[] + + /** + * Get the contents of a String as a byte[]. + * + * @param input the String to read from + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a String as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // byte[] -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // byte[] -> Writer + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the data to write + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final String encoding ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @param output The output buffer {@link Writer} + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> String + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param bufferSize size of internal buffer + * @param input the input bytes + * @return the created string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @param input input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> OutputStream + + /** + * Copy bytes from a byte[] to an OutputStream. + * + * @param input Input byte array. + * @param output output stream {@link OutputStream} + * @throws IOException in case of failure + * @deprecated inline this method + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final OutputStream output ) + throws IOException + { + output.write( input ); + } + + /** + * Compare the contents of two streams to determine if they are equal or not. + * + * @param input1 the first stream + * @param input2 the second stream + * @return true if the content of the streams are equal or they both don't exist, false otherwise + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.contentEquals()} + */ + @Deprecated + public static boolean contentEquals( @Nonnull final InputStream input1, @Nonnull final InputStream input2 ) + throws IOException + { + final InputStream bufferedInput1 = new BufferedInputStream( input1 ); + final InputStream bufferedInput2 = new BufferedInputStream( input2 ); + + int ch = bufferedInput1.read(); + while ( -1 != ch ) + { + final int ch2 = bufferedInput2.read(); + if ( ch != ch2 ) + { + return false; + } + ch = bufferedInput1.read(); + } + + final int ch2 = bufferedInput2.read(); + return -1 == ch2; + } + + // ---------------------------------------------------------------------- + // closeXXX() + // ---------------------------------------------------------------------- + + /** + * Closes a {@code Channel} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param channel The channel to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Channel channel ) + { + try + { + if ( channel != null ) + { + channel.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code InputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param inputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable InputStream inputStream ) + { + try + { + if ( inputStream != null ) + { + inputStream.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code OutputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param outputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable OutputStream outputStream ) + { + try + { + if ( outputStream != null ) + { + outputStream.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes a {@code Reader} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param reader The reader to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Reader reader ) + { + try + { + if ( reader != null ) + { + reader.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes a {@code Writer} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param writer The writer to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Writer writer ) + { + try + { + if ( writer != null ) + { + writer.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/Java7Support.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/Java7Support.java new file mode 100644 index 000000000..a51d50229 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/Java7Support.java @@ -0,0 +1,106 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import javax.annotation.Nonnull; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +/** + * Java7 feature detection + * + * @author Kristian Rosenvold + * + * @deprecated no longer needed, prefer to use {@link java.nio.file.Files} methods directly. + */ +@Deprecated +public class Java7Support +{ + /** + * @param file The file to check for being a symbolic link. + * @return true if the file is a symlink false otherwise. + */ + public static boolean isSymLink( @Nonnull File file ) + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * @param symlink The sym link. + * @return The file. + * @throws IOException in case of error. + */ + @Nonnull public static File readSymbolicLink( @Nonnull File symlink ) + throws IOException + { + return Files.readSymbolicLink( symlink.toPath() ).toFile(); + } + + /** + * @param file The file to check. + * @return true if exist false otherwise. + * @throws IOException in case of failure. + */ + public static boolean exists( @Nonnull File file ) + throws IOException + { + return Files.exists( file.toPath() ); + } + + /** + * @param symlink The link name. + * @param target The target. + * @return The linked file. + * @throws IOException in case of an error. + */ + @Nonnull public static File createSymbolicLink( @Nonnull File symlink, @Nonnull File target ) + throws IOException + { + return FileUtils.createSymbolicLink( symlink, target ); + } + + /** + * Performs a nio delete + * @param file the file to delete + * @throws IOException in case of error. + */ + public static void delete( @Nonnull File file ) + throws IOException + { + Files.delete( file.toPath() ); + } + + /** + * @return true in case of Java 7. + */ + public static boolean isJava7() + { + return true; + } + + /** + * @return true in case of Java7 or greater. + */ + public static boolean isAtLeastJava7() + { + return true; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java new file mode 100644 index 000000000..8abff427e --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java @@ -0,0 +1,155 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import java.util.regex.Pattern; + +import javax.annotation.Nonnull; + +/** + * Describes a match target for SelectorUtils. + *

          + * Significantly more efficient than using strings, since re-evaluation and re-tokenizing is avoided. + * + * @author Kristian Rosenvold + * @deprecated use {@code java.nio.filejava.nio.file.DirectoryStream.Filter} and related classes + */ +@Deprecated +public class MatchPattern +{ + private final String source; + + private final String regexPattern; + + private final Pattern regexPatternRegex; + + private final String separator; + + private final String[] tokenized; + + private MatchPattern( @Nonnull String source, @Nonnull String separator ) + { + regexPattern = SelectorUtils.isRegexPrefixedPattern( source ) ? source.substring( + SelectorUtils.REGEX_HANDLER_PREFIX.length(), + source.length() - SelectorUtils.PATTERN_HANDLER_SUFFIX.length() ) : null; + regexPatternRegex = regexPattern != null ? Pattern.compile( regexPattern ) : null; + this.source = SelectorUtils.isAntPrefixedPattern( source ) ? source.substring( + SelectorUtils.ANT_HANDLER_PREFIX.length(), + source.length() - SelectorUtils.PATTERN_HANDLER_SUFFIX.length() ) : source; + this.separator = separator; + tokenized = tokenizePathToString( this.source, separator ); + } + + + /** + * @param str The string to match for. + * @param isCaseSensitive case sensitive true false otherwise. + * @return true if matches false otherwise. + */ + public boolean matchPath( String str, boolean isCaseSensitive ) + { + if ( regexPattern != null ) + { + return regexPatternRegex.matcher( str ).matches(); + } + else + { + return SelectorUtils.matchAntPathPattern( this, str, separator, isCaseSensitive ); + } + } + + boolean matchPath( String str, String[] strDirs, boolean isCaseSensitive ) + { + if ( regexPattern != null ) + { + return regexPatternRegex.matcher( str ).matches(); + } + else + { + return SelectorUtils.matchAntPathPattern( getTokenizedPathString(), strDirs, isCaseSensitive ); + } + } + + /** + * @param str The string to check. + * @param isCaseSensitive Check case sensitive or not. + * @return true in case of matching pattern. + */ + public boolean matchPatternStart( @Nonnull String str, boolean isCaseSensitive ) + { + if ( regexPattern != null ) + { + // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgment until we have + // a file to deal with, or we can definitely say this is an exclusion... + return true; + } + else + { + String altStr = source.replace( '\\', '/' ); + + return SelectorUtils.matchAntPathPatternStart( this, str, File.separator, isCaseSensitive ) + || SelectorUtils.matchAntPathPatternStart( this, altStr, "/", isCaseSensitive ); + } + } + + /** + * @return Tokenized string. + */ + public String[] getTokenizedPathString() + { + return tokenized; + } + + + /** + * @param string The part which will be checked to start with. + * @return true in case of starting with the string false otherwise. + */ + public boolean startsWith( String string ) + { + return source.startsWith( string ); + } + + + static String[] tokenizePathToString( @Nonnull String path, @Nonnull String separator ) + { + List ret = new ArrayList(); + StringTokenizer st = new StringTokenizer( path, separator ); + while ( st.hasMoreTokens() ) + { + ret.add( st.nextToken() ); + } + return ret.toArray( new String[ret.size()] ); + } + + /** + * @param source The source. + * @return The match pattern. + */ + public static MatchPattern fromString( String source ) + { + return new MatchPattern( source, File.separator ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java new file mode 100644 index 000000000..693acb1cb --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java @@ -0,0 +1,96 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; + +import javax.annotation.Nonnull; + +/** + * A list of patterns to be matched + * + * @author Kristian Rosenvold + * @deprecated use {@code java.nio.filejava.nio.file.DirectoryStream.Filter} and related classes + */ +@Deprecated +public class MatchPatterns +{ + private final MatchPattern[] patterns; + + private MatchPatterns( @Nonnull MatchPattern... patterns ) + { + this.patterns = patterns; + } + + /** + * Checks these MatchPatterns against a specified string. + *

          + * Uses far less string tokenization than any of the alternatives. + * + * @param name The name to look for + * @param isCaseSensitive If the comparison is case sensitive + * @return true if any of the supplied patterns match + */ + public boolean matches( @Nonnull String name, boolean isCaseSensitive ) + { + String[] tokenized = MatchPattern.tokenizePathToString( name, File.separator ); + for ( MatchPattern pattern : patterns ) + { + if ( pattern.matchPath( name, tokenized, isCaseSensitive ) ) + { + return true; + } + } + return false; + } + + /** + * @param name The name. + * @param isCaseSensitive being case sensetive. + * @return true if any of the supplied patterns match start. + */ + public boolean matchesPatternStart( @Nonnull String name, boolean isCaseSensitive ) + { + for ( MatchPattern includesPattern : patterns ) + { + if ( includesPattern.matchPatternStart( name, isCaseSensitive ) ) + { + return true; + } + } + return false; + } + + /** + * @param sources The sources + * @return Converted match patterns. + */ + public static MatchPatterns from( @Nonnull String... sources ) + { + final int length = sources.length; + MatchPattern[] result = new MatchPattern[length]; + for ( int i = 0; i < length; i++ ) + { + result[i] = MatchPattern.fromString( sources[i] ); + } + return new MatchPatterns( result ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/ScanConductor.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/ScanConductor.java new file mode 100644 index 000000000..08d7a1cd3 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/ScanConductor.java @@ -0,0 +1,90 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; + +/** + *

          Visitor pattern for the DirectoryScanner. A ScanConductor controls the scanning process.

          + *

          + *

          Create an instance and pass it to + * {@link org.apache.maven.shared.utils.io.DirectoryScanner#setScanConductor(ScanConductor)}. + * You will get notified about every visited directory and file. You can use the {@link ScanAction} + * to control what should happen next.

          + *

          + *

          A ScanConductor might also store own information but users must make sure that the state gets + * cleaned between two scan() invocations.

          + * + * @author Mark Struberg + * + * @deprecated use {@code java.nio.file.Files.walkFileTree()} and related classes + */ +@Deprecated +public interface ScanConductor +{ + /** + * + */ + enum ScanAction + { + /** + * Abort the whole scanning process. The current file will not + * be added anymore. + */ + ABORT, + + /** + * Continue the scanning with the next item in the list. + */ + CONTINUE, + + /** + * This response is only valid for {@link ScanConductor#visitDirectory(String, java.io.File)}. + * Do not recurse into the current directory. The current directory will not be added + * and the processing will be continued with the next item in the list. + */ + NO_RECURSE, + + /** + * Abort processing the current directory. + * The current file will not be added. + * The processing will continue it's scan in the parent directory if any. + */ + ABORT_DIRECTORY + } + + /** + * This method will get invoked for every detected directory. + * + * @param name the directory name (contains parent folders up to the pwd) + * @param directory The directory. + * @return the ScanAction to control how to proceed with the scanning + */ + ScanAction visitDirectory( String name, File directory ); + + /** + * This method will get invoked for every detected file. + * + * @param name the file name (contains parent folders up to the pwd) + * @param file The file. + * @return the ScanAction to control how to proceed with the scanning + */ + ScanAction visitFile( String name, File file ); +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java new file mode 100644 index 000000000..b716e7c8a --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java @@ -0,0 +1,819 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; + +/** + *

          This is a utility class used by selectors and DirectoryScanner. The + * functionality more properly belongs just to selectors, but unfortunately + * DirectoryScanner exposed these as protected methods. Thus we have to + * support any subclasses of DirectoryScanner that may access these methods. + *

          + *

          This is a Singleton.

          + * + * @author Arnout J. Kuiper + * ajkuiper@wxs.nl + * @author Magesh Umasankar + * @author Bruce Atherton + * + * @deprecated use {@code java.nio.file.Files.walkFileTree()} and related classes + */ +@Deprecated +public final class SelectorUtils +{ + + /** + * Pattern handler prefix. + */ + private static final String PATTERN_HANDLER_PREFIX = "["; + + /** + * Pattern handler suffix. + */ + public static final String PATTERN_HANDLER_SUFFIX = "]"; + + /** + * Regex start pattern. + */ + public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX; + + /** + * ANT pattern prefix. + */ + public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX; + + /** + * Private Constructor + */ + private SelectorUtils() + { + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

          + * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart( String pattern, String str ) + { + return matchPatternStart( pattern, str, true ); + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

          + * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart( String pattern, String str, boolean isCaseSensitive ) + { + if ( isRegexPrefixedPattern( pattern ) ) + { + // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have + // a file to deal with, or we can definitely say this is an exclusion... + return true; + } + else + { + if ( isAntPrefixedPattern( pattern ) ) + { + pattern = pattern.substring( ANT_HANDLER_PREFIX.length(), + pattern.length() - PATTERN_HANDLER_SUFFIX.length() ); + } + + String altPattern = pattern.replace( '\\', '/' ); + String altStr = str.replace( '\\', '/' ); + + return matchAntPathPatternStart( altPattern, altStr, "/", isCaseSensitive ); + } + } + + private static boolean matchAntPathPatternStart( String pattern, String str, String separator, + boolean isCaseSensitive ) + { + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + if ( separatorPatternStartSlashMismatch( pattern, str, separator ) ) + { + return false; + } + + List patDirs = tokenizePath( pattern, separator ); + List strDirs = tokenizePath( str, separator ); + + int patIdxStart = 0; + int patIdxEnd = patDirs.size() - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.size() - 1; + + // up to first '**' + while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) + { + String patDir = patDirs.get( patIdxStart ); + if ( "**".equals( patDir ) ) + { + break; + } + if ( !match( patDir, strDirs.get( strIdxStart ), isCaseSensitive ) ) + { + return false; + } + patIdxStart++; + strIdxStart++; + } + + return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd; + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath( String pattern, String str ) + { + return matchPath( pattern, str, true ); + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath( String pattern, String str, boolean isCaseSensitive ) + { + if ( pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) + && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) ) + { + pattern = + pattern.substring( REGEX_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() ); + + return str.matches( pattern ); + } + else + { + if ( pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) + && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) ) + { + pattern = pattern.substring( ANT_HANDLER_PREFIX.length(), + pattern.length() - PATTERN_HANDLER_SUFFIX.length() ); + } + + return matchAntPathPattern( pattern, str, isCaseSensitive ); + } + } + + private static boolean matchAntPathPattern( String pattern, String str, boolean isCaseSensitive ) + { + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + if ( str.startsWith( File.separator ) != pattern.startsWith( File.separator ) ) + { + return false; + } + + List patDirs = tokenizePath( pattern, File.separator ); + List strDirs = tokenizePath( str, File.separator ); + + int patIdxStart = 0; + int patIdxEnd = patDirs.size() - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.size() - 1; + + // up to first '**' + while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) + { + String patDir = patDirs.get( patIdxStart ); + if ( "**".equals( patDir ) ) + { + break; + } + if ( !match( patDir, strDirs.get( strIdxStart ), isCaseSensitive ) ) + { + return false; + } + patIdxStart++; + strIdxStart++; + } + if ( strIdxStart > strIdxEnd ) + { + // String is exhausted + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !"**".equals( patDirs.get( i ) ) ) + { + return false; + } + } + return true; + } + else + { + if ( patIdxStart > patIdxEnd ) + { + // String not exhausted, but pattern is. Failure. + return false; + } + } + + // up to last '**' + while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) + { + String patDir = patDirs.get( patIdxEnd ); + if ( "**".equals( patDir ) ) + { + break; + } + if ( !match( patDir, strDirs.get( strIdxEnd ), isCaseSensitive ) ) + { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if ( strIdxStart > strIdxEnd ) + { + // String is exhausted + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !"**".equals( patDirs.get( i ) ) ) + { + return false; + } + } + return true; + } + + while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd ) + { + int patIdxTmp = -1; + for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ ) + { + if ( "**".equals( patDirs.get( i ) ) ) + { + patIdxTmp = i; + break; + } + } + if ( patIdxTmp == patIdxStart + 1 ) + { + // '**/**' situation, so skip one + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = ( patIdxTmp - patIdxStart - 1 ); + int strLength = ( strIdxEnd - strIdxStart + 1 ); + int foundIdx = -1; + strLoop: + for ( int i = 0; i <= strLength - patLength; i++ ) + { + for ( int j = 0; j < patLength; j++ ) + { + String subPat = patDirs.get( patIdxStart + j + 1 ); + String subStr = strDirs.get( strIdxStart + i + j ); + if ( !match( subPat, subStr, isCaseSensitive ) ) + { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } + + if ( foundIdx == -1 ) + { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !"**".equals( patDirs.get( i ) ) ) + { + return false; + } + } + + return true; + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:
          + * '*' means zero or more characters
          + * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match( String pattern, String str ) + { + return match( pattern, str, true ); + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:
          + * '*' means zero or more characters
          + * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match( String pattern, String str, boolean isCaseSensitive ) + { + char[] patArr = pattern.toCharArray(); + char[] strArr = str.toCharArray(); + int patIdxStart = 0; + int patIdxEnd = patArr.length - 1; + int strIdxStart = 0; + int strIdxEnd = strArr.length - 1; + char ch; + + boolean containsStar = false; + for ( char aPatArr : patArr ) + { + if ( aPatArr == '*' ) + { + containsStar = true; + break; + } + } + + if ( !containsStar ) + { + // No '*'s, so we make a shortcut + if ( patIdxEnd != strIdxEnd ) + { + return false; // Pattern and string do not have the same size + } + for ( int i = 0; i <= patIdxEnd; i++ ) + { + ch = patArr[i]; + if ( ch != '?' && !equals( ch, strArr[i], isCaseSensitive ) ) + { + return false; // Character mismatch + } + } + return true; // String matches against pattern + } + + if ( patIdxEnd == 0 ) + { + return true; // Pattern contains only '*', which matches anything + } + + // Process characters before first star + // CHECKSTYLE_OFF: InnerAssignment + while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd ) + // CHECKSTYLE_ON: InnerAssignment + { + if ( ch != '?' && !equals( ch, strArr[strIdxStart], isCaseSensitive ) ) + { + return false; // Character mismatch + } + patIdxStart++; + strIdxStart++; + } + if ( strIdxStart > strIdxEnd ) + { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( patArr[i] != '*' ) + { + return false; + } + } + return true; + } + + // Process characters after last star + // CHECKSTYLE_OFF: InnerAssignment + while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd ) + // CHECKSTYLE_ON: InnerAssignment + { + if ( ch != '?' && !equals( ch, strArr[strIdxEnd], isCaseSensitive ) ) + { + return false; // Character mismatch + } + patIdxEnd--; + strIdxEnd--; + } + if ( strIdxStart > strIdxEnd ) + { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( patArr[i] != '*' ) + { + return false; + } + } + return true; + } + + // process pattern between stars. padIdxStart and patIdxEnd point + // always to a '*'. + while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd ) + { + int patIdxTmp = -1; + for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ ) + { + if ( patArr[i] == '*' ) + { + patIdxTmp = i; + break; + } + } + if ( patIdxTmp == patIdxStart + 1 ) + { + // Two stars next to each other, skip the first one. + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = ( patIdxTmp - patIdxStart - 1 ); + int strLength = ( strIdxEnd - strIdxStart + 1 ); + int foundIdx = -1; + strLoop: + for ( int i = 0; i <= strLength - patLength; i++ ) + { + for ( int j = 0; j < patLength; j++ ) + { + ch = patArr[patIdxStart + j + 1]; + if ( ch != '?' && !equals( ch, strArr[strIdxStart + i + j], isCaseSensitive ) ) + { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } + + if ( foundIdx == -1 ) + { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + // All characters in the string are used. Check if only '*'s are left + // in the pattern. If so, we succeeded. Otherwise failure. + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( patArr[i] != '*' ) + { + return false; + } + } + return true; + } + + /** + * Tests whether two characters are equal. + */ + private static boolean equals( char c1, char c2, boolean isCaseSensitive ) + { + if ( c1 == c2 ) + { + return true; + } + if ( !isCaseSensitive ) + { + // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase() + if ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 ) + || Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) ) + { + return true; + } + } + return false; + } + + /** + * Breaks a path up into a List of path elements, tokenizing on + * File.separator. + * + * @param path Path to tokenize. Must not be null. + * @param separator The separator to use + * @return a List of path elements from the tokenized path + */ + private static List tokenizePath( String path, String separator ) + { + List ret = new ArrayList(); + StringTokenizer st = new StringTokenizer( path, separator ); + while ( st.hasMoreTokens() ) + { + ret.add( st.nextToken() ); + } + return ret; + } + + + static boolean matchAntPathPatternStart( @Nonnull MatchPattern pattern, + @Nonnull String str, + @Nonnull String separator, + boolean isCaseSensitive ) + { + return !separatorPatternStartSlashMismatch( pattern, str, separator ) + && matchAntPathPatternStart( pattern.getTokenizedPathString(), str, separator, isCaseSensitive ); + } + + private static String[] tokenizePathToString( @Nonnull String path, @Nonnull String separator ) + { + List ret = new ArrayList(); + StringTokenizer st = new StringTokenizer( path, separator ); + while ( st.hasMoreTokens() ) + { + ret.add( st.nextToken() ); + } + return ret.toArray( new String[ret.size()] ); + } + + private static boolean matchAntPathPatternStart( @Nonnull String[] patDirs, + @Nonnull String str, + @Nonnull String separator, + boolean isCaseSensitive ) + { + String[] strDirs = tokenizePathToString( str, separator ); + return matchAntPathPatternStart( patDirs, strDirs, isCaseSensitive ); + } + + private static boolean matchAntPathPatternStart( @Nonnull String[] patDirs, + @Nonnull String[] tokenizedFileName, + boolean isCaseSensitive ) + { + + int patIdxStart = 0; + int patIdxEnd = patDirs.length - 1; + int strIdxStart = 0; + int strIdxEnd = tokenizedFileName.length - 1; + + // up to first '**' + while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) + { + String patDir = patDirs[patIdxStart]; + if ( patDir.equals( "**" ) ) + { + break; + } + if ( !match( patDir, tokenizedFileName[strIdxStart], isCaseSensitive ) ) + { + return false; + } + patIdxStart++; + strIdxStart++; + } + + return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd; + } + + private static boolean separatorPatternStartSlashMismatch( @Nonnull MatchPattern matchPattern, @Nonnull String str, + @Nonnull String separator ) + { + return str.startsWith( separator ) != matchPattern.startsWith( separator ); + } + + private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator ) + { + return str.startsWith( separator ) != pattern.startsWith( separator ); + } + + + static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive ) + { + int patIdxStart = 0; + int patIdxEnd = patDirs.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) + { + String patDir = patDirs[patIdxStart]; + if ( patDir.equals( "**" ) ) + { + break; + } + if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) ) + { + return false; + } + patIdxStart++; + strIdxStart++; + } + if ( strIdxStart > strIdxEnd ) + { + // String is exhausted + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !patDirs[i].equals( "**" ) ) + { + return false; + } + } + return true; + } + else + { + if ( patIdxStart > patIdxEnd ) + { + // String not exhausted, but pattern is. Failure. + return false; + } + } + + // up to last '**' + while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) + { + String patDir = patDirs[patIdxEnd]; + if ( patDir.equals( "**" ) ) + { + break; + } + if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) ) + { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if ( strIdxStart > strIdxEnd ) + { + // String is exhausted + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !patDirs[i].equals( "**" ) ) + { + return false; + } + } + return true; + } + + while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd ) + { + int patIdxTmp = -1; + for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ ) + { + if ( patDirs[i].equals( "**" ) ) + { + patIdxTmp = i; + break; + } + } + if ( patIdxTmp == patIdxStart + 1 ) + { + // '**/**' situation, so skip one + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = ( patIdxTmp - patIdxStart - 1 ); + int strLength = ( strIdxEnd - strIdxStart + 1 ); + int foundIdx = -1; + strLoop: + for ( int i = 0; i <= strLength - patLength; i++ ) + { + for ( int j = 0; j < patLength; j++ ) + { + String subPat = patDirs[patIdxStart + j + 1]; + String subStr = strDirs[strIdxStart + i + j]; + if ( !match( subPat, subStr, isCaseSensitive ) ) + { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } + + if ( foundIdx == -1 ) + { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !patDirs[i].equals( "**" ) ) + { + return false; + } + } + + return true; + } + + static boolean isRegexPrefixedPattern( String pattern ) + { + return pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) + && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ); + } + + static boolean isAntPrefixedPattern( String pattern ) + { + return pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) + && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ); + } + + static boolean matchAntPathPattern( @Nonnull MatchPattern matchPattern, @Nonnull String str, + @Nonnull String separator, boolean isCaseSensitive ) + { + if ( separatorPatternStartSlashMismatch( matchPattern, str, separator ) ) + { + return false; + } + String[] patDirs = matchPattern.getTokenizedPathString(); + String[] strDirs = tokenizePathToString( str, separator ); + return matchAntPathPattern( patDirs, strDirs, isCaseSensitive ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/WalkCollector.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/WalkCollector.java new file mode 100644 index 000000000..3db835aea --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/io/WalkCollector.java @@ -0,0 +1,83 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @deprecated use {@code java.nio.file.FileVisitor} and related classes + */ +@Deprecated +public class WalkCollector + implements DirectoryWalkListener +{ + final List steps; + + File startingDir; + + int startCount; + + int finishCount; + + int percentageLow; + + int percentageHigh; + + /** + * Create an instance. + */ + public WalkCollector() + { + steps = new ArrayList(); + startCount = 0; + finishCount = 0; + percentageLow = 0; + percentageHigh = 0; + } + + /** {@inheritDoc} */ + public void debug( String message ) + { + // can be used to set some message + } + + /** {@inheritDoc} */ + public void directoryWalkStarting( File basedir ) + { + startingDir = basedir; + startCount++; + } + + /** {@inheritDoc} */ + public void directoryWalkStep( int percentage, File file ) + { + steps.add( file ); + percentageLow = Math.min( percentageLow, percentage ); + percentageHigh = Math.max( percentageHigh, percentage ); + } + + /** {@inheritDoc} */ + public void directoryWalkFinished() + { + finishCount++; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/AnsiMessageBuilder.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/AnsiMessageBuilder.java new file mode 100644 index 000000000..2d59bc9dc --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/AnsiMessageBuilder.java @@ -0,0 +1,156 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +import org.fusesource.jansi.Ansi; + +/** + * Message builder implementation that supports ANSI colors through + * Jansi with configurable styles through {@link Style}. + */ +class AnsiMessageBuilder + implements MessageBuilder, LoggerLevelRenderer +{ + private Ansi ansi; + + AnsiMessageBuilder() + { + this( Ansi.ansi() ); + } + + AnsiMessageBuilder( StringBuilder builder ) + { + this( Ansi.ansi( builder ) ); + } + + AnsiMessageBuilder( int size ) + { + this( Ansi.ansi( size ) ); + } + + AnsiMessageBuilder( Ansi ansi ) + { + this.ansi = ansi; + } + + public String debug( String level ) + { + return Style.DEBUG.apply( ansi ).a( level ).reset().toString(); + } + + public String info( String level ) + { + return Style.INFO.apply( ansi ).a( level ).reset().toString(); + } + + public String warning( String level ) + { + return Style.WARNING.apply( ansi ).a( level ).reset().toString(); + } + + public String error( String level ) + { + return Style.ERROR.apply( ansi ).a( level ).reset().toString(); + } + + public AnsiMessageBuilder success( Object message ) + { + Style.SUCCESS.apply( ansi ).a( message ).reset(); + return this; + } + + public AnsiMessageBuilder warning( Object message ) + { + Style.WARNING.apply( ansi ).a( message ).reset(); + return this; + } + + public AnsiMessageBuilder failure( Object message ) + { + Style.FAILURE.apply( ansi ).a( message ).reset(); + return this; + } + + public AnsiMessageBuilder strong( Object message ) + { + Style.STRONG.apply( ansi ).a( message ).reset(); + return this; + } + + public AnsiMessageBuilder mojo( Object message ) + { + Style.MOJO.apply( ansi ).a( message ).reset(); + return this; + } + + public AnsiMessageBuilder project( Object message ) + { + Style.PROJECT.apply( ansi ).a( message ).reset(); + return this; + } + + public AnsiMessageBuilder a( char[] value, int offset, int len ) + { + ansi.a( value, offset, len ); + return this; + } + + public AnsiMessageBuilder a( char[] value ) + { + ansi.a( value ); + return this; + } + + public AnsiMessageBuilder a( CharSequence value, int start, int end ) + { + ansi.a( value, start, end ); + return this; + } + + public AnsiMessageBuilder a( CharSequence value ) + { + ansi.a( value ); + return this; + } + + public AnsiMessageBuilder a( Object value ) + { + ansi.a( value ); + return this; + } + + public AnsiMessageBuilder newline() + { + ansi.newline(); + return this; + } + + public AnsiMessageBuilder format( String pattern, Object... args ) + { + ansi.format( pattern, args ); + return this; + } + + @Override + public String toString() + { + return ansi.toString(); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/LoggerLevelRenderer.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/LoggerLevelRenderer.java new file mode 100644 index 000000000..9dac0eb17 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/LoggerLevelRenderer.java @@ -0,0 +1,53 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +/** + * Logger level renderer, intended for Maven slf4j logging provider implementers to render + * logger level. + * + * @since 3.2.0 + */ +public interface LoggerLevelRenderer +{ + /** + * Render DEBUG level. + * By default, bold cyan + */ + String debug( String level ); + + /** + * Render INFO level. + * By default, bold blue + */ + String info( String level ); + + /** + * Render WARNING level. + * By default, bold yellow + */ + String warning( String level ); + + /** + * Render ERROR level. + * By default, bold red + */ + String error( String level ); +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/MessageBuilder.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/MessageBuilder.java new file mode 100644 index 000000000..060e824eb --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/MessageBuilder.java @@ -0,0 +1,103 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +/** + * Message builder that supports configurable styling. + * @see MessageUtils + * @since 3.1.0 + */ +public interface MessageBuilder +{ + /** + * Append message content in success style. + * By default, bold green + */ + MessageBuilder success( Object message ); + + /** + * Append message content in warning style. + * By default, bold yellow + */ + MessageBuilder warning( Object message ); + + /** + * Append message content in failure style. + * By default, bold red + */ + MessageBuilder failure( Object message ); + + /** + * Append message content in strong style. + * By default, bold + */ + MessageBuilder strong( Object message ); + + /** + * Append message content in mojo style. + * By default, green + */ + MessageBuilder mojo( Object message ); + + /** + * Append message content in project style. + * By default, cyan + */ + MessageBuilder project( Object message ); + + // + // message building methods modelled after Ansi methods + // + /** + * Append content to the message buffer. + */ + MessageBuilder a( char[] value, int offset, int len ); + + /** + * Append content to the message buffer. + */ + MessageBuilder a( char[] value ); + + /** + * Append content to the message buffer. + */ + MessageBuilder a( CharSequence value, int start, int end ); + + /** + * Append content to the message buffer. + */ + MessageBuilder a( CharSequence value ); + + /** + * Append content to the message buffer. + */ + MessageBuilder a( Object value ); + + /** + * Append newline to the message buffer. + */ + MessageBuilder newline(); + + /** + * Append formatted content to the buffer. + * @see String#format(String, Object...) + */ + MessageBuilder format( String pattern, Object... args ); +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/MessageUtils.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/MessageUtils.java new file mode 100644 index 000000000..74b85a42c --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/MessageUtils.java @@ -0,0 +1,204 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.AnsiConsole; + +/** + * Colored message utils, to manage colors consistently across plugins (only if Maven version is at least 3.5.0). + * For Maven version before 3.5.0, message built with this util will never add color. + *

          + * Internally, Jansi is used to render + * ANSI colors on any platform. + * @since 3.1.0 + */ +public class MessageUtils +{ + private static final boolean JANSI; + + /** Reference to the JVM shutdown hook, if registered */ + private static Thread shutdownHook; + + /** Synchronization monitor for the "uninstall" */ + private static final Object STARTUP_SHUTDOWN_MONITOR = new Object(); + + static + { + boolean jansi = true; + try + { + // JAnsi is provided by Maven core since 3.5.0 + Class.forName( "org.fusesource.jansi.Ansi" ); + } + catch ( ClassNotFoundException cnfe ) + { + jansi = false; + } + JANSI = jansi; + } + + /** + * Install color support. + * This method is called by Maven core, and calling it is not necessary in plugins. + */ + public static void systemInstall() + { + if ( JANSI ) + { + AnsiConsole.systemInstall(); + } + } + + /** + * Undo a previous {@link #systemInstall()}. If {@link #systemInstall()} was called + * multiple times, {@link #systemUninstall()} must be called call the same number of times before + * it is actually uninstalled. + */ + public static void systemUninstall() + { + synchronized ( STARTUP_SHUTDOWN_MONITOR ) + { + doSystemUninstall(); + + // hook can only set when JANSI is true + if ( shutdownHook != null ) + { + // if out and system_out are same instance again, ansi is assumed to be uninstalled + if ( AnsiConsole.out == AnsiConsole.system_out ) + { + try + { + Runtime.getRuntime().removeShutdownHook( shutdownHook ); + } + catch ( IllegalStateException ex ) + { + // ignore - VM is already shutting down + } + } + } + } + } + + private static void doSystemUninstall() + { + if ( JANSI ) + { + AnsiConsole.systemUninstall(); + } + } + + /** + * Enables message color (if JAnsi is available). + * @param flag + */ + public static void setColorEnabled( boolean flag ) + { + if ( JANSI ) + { + Ansi.setEnabled( flag ); + } + } + + /** + * Is message color enabled: requires JAnsi available (through Maven) and the color has not been disabled. + */ + public static boolean isColorEnabled() + { + return JANSI ? Ansi.isEnabled() : false; + } + + /** + * Create a default message buffer. + * @return a new buffer + */ + public static MessageBuilder buffer() + { + return JANSI ? new AnsiMessageBuilder() : new PlainMessageBuilder(); + } + + /** + * Create a message buffer with defined String builder. + * @return a new buffer + */ + public static MessageBuilder buffer( StringBuilder builder ) + { + return JANSI ? new AnsiMessageBuilder( builder ) : new PlainMessageBuilder( builder ); + } + + /** + * Create a message buffer with an internal buffer of defined size. + * @return a new buffer + */ + public static MessageBuilder buffer( int size ) + { + return JANSI ? new AnsiMessageBuilder( size ) : new PlainMessageBuilder( size ); + } + + /** + * Create a logger level renderer. + * @return a logger level renderer + * @since 3.2.0 + */ + @SuppressWarnings( "checkstyle:magicnumber" ) + public static LoggerLevelRenderer level() + { + return JANSI ? new AnsiMessageBuilder( 20 ) : new PlainMessageBuilder( 7 ); + } + + /** + * Remove any ANSI code from a message (colors or other escape sequences). + * @param msg message eventually containing ANSI codes + * @return the message with ANSI codes removed + */ + public static String stripAnsiCodes( String msg ) + { + return msg.replaceAll( "\u001B\\[[;\\d]*[ -/]*[@-~]", "" ); + } + + /** + * Register a shutdown hook with the JVM runtime, uninstalling Ansi support on + * JVM shutdown unless is has already been uninstalled at that time. + *

          Delegates to {@link #doSystemUninstall()} for the actual uninstall procedure + * + * @see Runtime#addShutdownHook(Thread) + * @see MessageUtils#systemUninstall() + * @see #doSystemUninstall() + */ + public static void registerShutdownHook() + { + if ( JANSI && shutdownHook == null ) + { + // No shutdown hook registered yet. + shutdownHook = new Thread() + { + @Override + public void run() + { + synchronized ( STARTUP_SHUTDOWN_MONITOR ) + { + doSystemUninstall(); + } + } + }; + Runtime.getRuntime().addShutdownHook( shutdownHook ); + } + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/PlainMessageBuilder.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/PlainMessageBuilder.java new file mode 100644 index 000000000..6a7b56e1b --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/PlainMessageBuilder.java @@ -0,0 +1,142 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +/** + * Message builder implementation that just ignores styling, for Maven version earlier than 3.5.0. + */ +class PlainMessageBuilder + implements MessageBuilder, LoggerLevelRenderer +{ + private StringBuilder buffer; + + PlainMessageBuilder() + { + buffer = new StringBuilder(); + } + + PlainMessageBuilder( StringBuilder builder ) + { + buffer = builder; + } + + PlainMessageBuilder( int size ) + { + buffer = new StringBuilder( size ); + } + + public String debug( String level ) + { + return a( level ).toString(); + } + + public String info( String level ) + { + return a( level ).toString(); + } + + public String warning( String level ) + { + return a( level ).toString(); + } + + public String error( String level ) + { + return a( level ).toString(); + } + + public PlainMessageBuilder success( Object message ) + { + return a( message ); + } + + public PlainMessageBuilder warning( Object message ) + { + return a( message ); + } + + public PlainMessageBuilder failure( Object message ) + { + return a( message ); + } + + public PlainMessageBuilder strong( Object message ) + { + return a( message ); + } + + public PlainMessageBuilder mojo( Object message ) + { + return a( message ); + } + + public PlainMessageBuilder project( Object message ) + { + return a( message ); + } + + public PlainMessageBuilder a( char[] value, int offset, int len ) + { + buffer.append( value, offset, len ); + return this; + } + + public PlainMessageBuilder a( char[] value ) + { + buffer.append( value ); + return this; + } + + public PlainMessageBuilder a( CharSequence value, int start, int end ) + { + buffer.append( value, start, end ); + return this; + } + + public PlainMessageBuilder a( CharSequence value ) + { + buffer.append( value ); + return this; + } + + public PlainMessageBuilder a( Object value ) + { + buffer.append( value ); + return this; + } + + public PlainMessageBuilder newline() + { + buffer.append( System.getProperty( "line.separator" ) ); + return this; + } + + public PlainMessageBuilder format( String pattern, Object... args ) + { + buffer.append( String.format( pattern, args ) ); + return this; + } + + @Override + public String toString() + { + return buffer.toString(); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/Style.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/Style.java new file mode 100644 index 000000000..756ff1411 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/Style.java @@ -0,0 +1,182 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.Ansi.Color; + +import java.util.Locale; + +/** + * Configurable message styles. + * @since 3.1.0 + */ +enum Style +{ + + DEBUG( "bold,cyan" ), + INFO( "bold,blue" ), + WARNING( "bold,yellow" ), + ERROR( "bold,red" ), + SUCCESS( "bold,green" ), + FAILURE( "bold,red" ), + STRONG( "bold" ), + MOJO( "green" ), + PROJECT( "cyan" ); + + private final boolean bold; + + private final boolean bright; + + private final Color color; + + private final boolean bgBright; + + private final Color bgColor; + + Style( String defaultValue ) + { + boolean currentBold = false; + boolean currentBright = false; + Color currentColor = null; + boolean currentBgBright = false; + Color currentBgColor = null; + + String value = System.getProperty( "style." + name().toLowerCase( Locale.ENGLISH ), + defaultValue ).toLowerCase( Locale.ENGLISH ); + + for ( String token : value.split( "," ) ) + { + if ( "bold".equals( token ) ) + { + currentBold = true; + } + else if ( token.startsWith( "bg" ) ) + { + token = token.substring( 2 ); + if ( token.startsWith( "bright" ) ) + { + currentBgBright = true; + token = token.substring( 6 ); + } + currentBgColor = toColor( token ); + } + else + { + if ( token.startsWith( "bright" ) ) + { + currentBright = true; + token = token.substring( 6 ); + } + currentColor = toColor( token ); + } + } + + this.bold = currentBold; + this.bright = currentBright; + this.color = currentColor; + this.bgBright = currentBgBright; + this.bgColor = currentBgColor; + } + + private static Color toColor( String token ) + { + for ( Color color : Color.values() ) + { + if ( color.toString().equalsIgnoreCase( token ) ) + { + return color; + } + } + return null; + } + + Ansi apply( Ansi ansi ) + { + if ( bold ) + { + ansi.bold(); + } + if ( color != null ) + { + if ( bright ) + { + ansi.fgBright( color ); + } + else + { + ansi.fg( color ); + } + } + if ( bgColor != null ) + { + if ( bgBright ) + { + ansi.bgBright( bgColor ); + } + else + { + ansi.bg( bgColor ); + } + } + return ansi; + } + + @Override + public String toString() + { + if ( !bold && color == null && bgColor == null ) + { + return name(); + } + StringBuilder sb = new StringBuilder( name() + '=' ); + if ( bold ) + { + sb.append( "bold" ); + } + if ( color != null ) + { + if ( sb.length() > 0 ) + { + sb.append( ',' ); + } + if ( bright ) + { + sb.append( "bright" ); + } + sb.append( color.name() ); + } + if ( bgColor != null ) + { + if ( sb.length() > 0 ) + { + sb.append( ',' ); + } + sb.append( "bg" ); + if ( bgBright ) + { + sb.append( "bright" ); + } + sb.append( bgColor.name() ); + } + return sb.toString(); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/package-info.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/package-info.java new file mode 100644 index 000000000..dfc5ddeed --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/logging/package-info.java @@ -0,0 +1,51 @@ +// CHECKSTYLE_OFF: RegexpHeader +/* + * 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. + */ +/** + * An API to write Maven messages to console with styled color content, consistently across whole + * Maven ecosystem (Maven itself or any plugin or extension). + *

          + * Messages are built with instances of {@link org.apache.maven.shared.utils.logging.MessageBuilder MessageBuilder} + * which provides a fluent API, while error level are colored by slf4j provider with + * {@link org.apache.maven.shared.utils.logging.LoggerLevelRenderer LoggerLevelRenderer}. + *

          + * {@link org.apache.maven.shared.utils.logging.MessageUtils MessageUtils} gives access to these builders. + *

          + * Plugins can use this API with any Maven version: color + * just won't be activated when run with Maven version older than 3.5.0. + *

          + * Styles are:

            + *
          • debug, info, warning and error for + * {@link org.apache.maven.shared.utils.logging.LoggerLevelRenderer logger level rendering},
          • + *
          • success, warning, failure, strong, mojo + * and project for {@link org.apache.maven.shared.utils.logging.MessageBuilder message content}
          • + *
          + * Default styles colors can be overridden through system properties, that can be set in MAVEN_OPTS + * environment variable (eventually in .mavenrc script):
            + *
          • system properties are named style.<style name>,
          • + *
          • values are comma separated combination of bold, <color> and + * bg<color> (for background), where <color> is + * an ANSI color: black, + * red, green, yellow, blue, magenta, + * cyan or white, eventually with bright prefix
          • + *
          + * @since 3.1.0 + */ +package org.apache.maven.shared.utils.logging; + diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java new file mode 100644 index 000000000..851cffbf2 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java @@ -0,0 +1,393 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.ArrayList; +import org.apache.maven.shared.utils.Os; + +/** + * XMLWriter with nice indentation. + * + * @author kama + */ +public class PrettyPrintXMLWriter + implements XMLWriter +{ + private static final char[] CLOSE_1 = "/>".toCharArray(); + + private static final char[] CLOSE_2 = " elementStack = new ArrayList(); + + private boolean processingElement = false; + + private boolean documentStarted = false; + + private boolean endOnSameLine = false; + + private int depth = 0; + + private char[] lineIndent; + + private char[] lineSeparator; + + private String encoding; + + private String docType; + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + */ + public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent ) + { + this( writer, lineIndent, null, null ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + */ + public PrettyPrintXMLWriter( Writer writer, String lineIndent ) + { + this( new PrintWriter( writer ), lineIndent ); + } + + /** + * @param writer not null + */ + public PrettyPrintXMLWriter( PrintWriter writer ) + { + this( writer, null, null ); + } + + /** + * @param writer not null + */ + public PrettyPrintXMLWriter( Writer writer ) + { + this( new PrintWriter( writer ) ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param encoding could be null or invalid. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent, String encoding, String doctype ) + { + this( writer, lineIndent.toCharArray(), Os.LINE_SEP.toCharArray(), encoding, doctype ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param encoding could be null or invalid. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( Writer writer, String lineIndent, String encoding, String doctype ) + { + this( new PrintWriter( writer ), lineIndent, encoding, doctype ); + } + + /** + * @param writer not null + * @param encoding could be null or invalid. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( PrintWriter writer, String encoding, String doctype ) + { + this( writer, DEFAULT_LINE_INDENT, Os.LINE_SEP.toCharArray(), encoding, doctype ); + } + + /** + * @param writer not null + * @param encoding could be null or invalid. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( Writer writer, String encoding, String doctype ) + { + this( new PrintWriter( writer ), encoding, doctype ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param lineSeparator could be null, but the normal way is valid line separator + * @param encoding could be null or the encoding to use. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent, String lineSeparator, String encoding, + String doctype ) + { + this( writer, lineIndent.toCharArray(), lineSeparator.toCharArray(), encoding, doctype ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param lineSeparator could be null, but the normal way is valid line separator + * @param encoding could be null or the encoding to use. + * @param doctype could be null. + */ + private PrettyPrintXMLWriter( PrintWriter writer, char[] lineIndent, char[] lineSeparator, String encoding, + String doctype ) + { + super(); + this.writer = writer; + this.lineIndent = lineIndent; + this.lineSeparator = lineSeparator; + this.encoding = encoding; + this.docType = doctype; + + depth = 0; + + // Fail early with assertions enabled. Issue is in the calling code not having checked for any errors. + assert !writer.checkError() : "Unexpected error state PrintWriter passed to PrettyPrintXMLWriter."; + } + + /** {@inheritDoc} */ + public void addAttribute( String key, String value ) throws IOException + { + if ( !processingElement ) + { + throw new IllegalStateException( "currently processing no element" ); + } + + writer.write( ' ' ); + writer.write( key ); + writer.write( '=' ); + XMLEncode.xmlEncodeTextAsPCDATA( value, true, '"', writer ); + if ( writer.checkError() ) + { + throw new IOException( "Failure adding attribute '" + key + "' with value '" + value + "'" ); + } + } + + /** {@inheritDoc} */ + public void setEncoding( String encoding ) + { + if ( documentStarted ) + { + throw new IllegalStateException( "Document headers already written!" ); + } + + this.encoding = encoding; + } + + /** {@inheritDoc} */ + public void setDocType( String docType ) + { + if ( documentStarted ) + { + throw new IllegalStateException( "Document headers already written!" ); + } + + this.docType = docType; + } + + /** + * @param lineSeparator The line separator to be used. + */ + public void setLineSeparator( String lineSeparator ) + { + if ( documentStarted ) + { + throw new IllegalStateException( "Document headers already written!" ); + } + + this.lineSeparator = lineSeparator.toCharArray(); + } + + /** + * @param lineIndentParameter The line indent parameter. + */ + public void setLineIndenter( String lineIndentParameter ) + { + if ( documentStarted ) + { + throw new IllegalStateException( "Document headers already written!" ); + } + + this.lineIndent = lineIndentParameter.toCharArray(); + } + + /** {@inheritDoc} */ + public void startElement( String elementName ) throws IOException + { + boolean firstLine = ensureDocumentStarted(); + + completePreviouslyOpenedElement(); + + if ( !firstLine ) + { + newLine(); + } + + writer.write( '<' ); + writer.write( elementName ); + if ( writer.checkError() ) + { + throw new IOException( "Failure starting element '" + elementName + "'." ); + } + + processingElement = true; + + elementStack.add( depth++, elementName ); + } + + /** {@inheritDoc} */ + public void writeText( String text ) throws IOException + { + ensureDocumentStarted(); + + completePreviouslyOpenedElement(); + + XMLEncode.xmlEncodeText( text, writer ); + + endOnSameLine = true; + + if ( writer.checkError() ) + { + throw new IOException( "Failure writing text." ); + } + } + + /** {@inheritDoc} */ + public void writeMarkup( String markup ) throws IOException + { + ensureDocumentStarted(); + + completePreviouslyOpenedElement(); + + writer.write( markup ); + + if ( writer.checkError() ) + { + throw new IOException( "Failure writing markup." ); + } + } + + /** {@inheritDoc} */ + public void endElement() throws IOException + { + String chars = elementStack.get( --depth ); + if ( processingElement ) + { + // this means we don't have any content yet so we just add a /> + writer.write( CLOSE_1 ); + + processingElement = false; + } + else + { + if ( !endOnSameLine ) + { + newLine(); + } + + // otherwise we need a full closing tag for that element + writer.write( CLOSE_2 ); + writer.write( chars ); + writer.write( '>' ); + } + + endOnSameLine = false; + + if ( writer.checkError() ) + { + throw new IOException( "Failure ending element." ); + } + } + + /** + * Write the documents if not already done. + * + * @return true if the document headers have freshly been written. + */ + private boolean ensureDocumentStarted() + { + if ( !documentStarted ) + { + if ( docType != null || encoding != null ) + { + writeDocumentHeader(); + } + + documentStarted = true; + + return true; + } + + return false; + } + + private void writeDocumentHeader() + { + writer.write( "" ); + + newLine(); + + if ( docType != null ) + { + writer.write( "' ); + newLine(); + } + } + + private void newLine() + { + writer.write( lineSeparator ); + + for ( int i = 0; i < depth; i++ ) + { + writer.write( lineIndent ); + } + } + + private void completePreviouslyOpenedElement() + { + if ( processingElement ) + { + writer.write( '>' ); + processingElement = false; + } + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java new file mode 100644 index 000000000..0d6ecd165 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XMLEncode.java @@ -0,0 +1,378 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; + +/** + * Collection of XML encoding/decoding helpers.
          + * This is all about the special characters & and <, and for attributes + * " and '. These must be encoded/decoded from/to XML. + */ +final class XMLEncode +{ + + private static final int CDATA_BLOCK_THRESHOLD_LENGTH = 12; + + private static final char DEFAULT_QUOTE_CHAR = '"'; + + /** + * Checks if this text purely consists of the white space characters + * ' ', TAB, NEWLINE. + */ + public static boolean isWhiteSpace( String text ) + { + for ( int i = 0; i < text.length(); i++ ) + { + char c = text.charAt( i ); + if ( !Character.isWhitespace( c ) ) + { + return false; + } + } + return true; + } + + /** + * Makes any text fit into XML attributes. + */ + public static String xmlEncodeTextForAttribute( String text, char quoteChar ) + { + if ( text == null ) + { + return null; + } + return xmlEncodeTextAsPCDATA( text, true, quoteChar ); + } + + /** + * Encodes text as XML in the most suitable way, either CDATA block or PCDATA. + */ + public static String xmlEncodeText( String text ) + { + if ( text == null ) + { + return null; + } + StringWriter writer = new StringWriter( text.length() * 2 ); + xmlEncodeText( text, writer ); + return writer.toString(); + } + + public static void xmlEncodeText( String text, Writer writer ) + { + if ( text == null ) + { + return; + } + try + { + if ( !needsEncoding( text ) ) + { + writer.write( text ); + return; + } + else + { + // only encode as cdata if is is longer than CDATA block overhead: + if ( text.length() > CDATA_BLOCK_THRESHOLD_LENGTH ) + { + String cdata = xmlEncodeTextAsCDATABlock( text ); + if ( cdata != null ) + { + writer.write( cdata ); + return; + } + } + } + } + catch ( IOException e ) + { + throw new RuntimeException( e ); + } + // if every thing else fails, do it the save way... + xmlEncodeTextAsPCDATA( text, false, DEFAULT_QUOTE_CHAR, writer ); + } + + /** + * Encodes any text as PCDATA. + */ + public static String xmlEncodeTextAsPCDATA( String text ) + { + if ( text == null ) + { + return null; + } + return xmlEncodeTextAsPCDATA( text, false ); + } + + /** + * Encodes any text as PCDATA. + * + * @param forAttribute if you want + * quotes and apostrophes specially treated for attributes + */ + public static String xmlEncodeTextAsPCDATA( String text, boolean forAttribute ) + { + return xmlEncodeTextAsPCDATA( text, forAttribute, DEFAULT_QUOTE_CHAR ); + } + + /** + * Encodes any text as PCDATA. + * + * @param forAttribute if you want + * quotes and apostrophes specially treated for attributes + * @param quoteChar if this is for attributes this char is used to quote the attribute value + */ + public static String xmlEncodeTextAsPCDATA( String text, boolean forAttribute, char quoteChar ) + { + if ( text == null ) + { + return null; + } + StringWriter writer = new StringWriter( text.length() * 2 ); + xmlEncodeTextAsPCDATA( text, forAttribute, quoteChar, writer ); + return writer.toString(); + } + + public static void xmlEncodeTextAsPCDATA( String text, boolean forAttribute, char quoteChar, Writer n ) + { + if ( text == null ) + { + return; + } + try + { + char c; + int length = text.length(); + if ( forAttribute ) + { + n.append( quoteChar ); + } + + for ( int i = 0; i < length; i++ ) + { + c = text.charAt( i ); + switch ( c ) + { + case '&': + n.append( "&" ); + break; + case '<': + n.append( "<" ); + break; + case '>': // FIX for sourceforge bug #802520 ("]]>" needs encoding) + n.append( ">" ); + break; + case '"': + if ( forAttribute ) + { + n.append( """ ); + } + else + { + n.append( c ); + } + break; + case '\'': + if ( forAttribute ) + { + n.append( "'" ); + } + else + { + n.append( c ); + } + break; + case '\r': + if ( forAttribute ) + { + if ( i == ( length - 1 ) || text.charAt( i + 1 ) != '\n' ) + { + n.append( " " ); + } + } + else + { + n.append( c ); + } + // but skip the \r in \r\n + + break; + case '\n': + if ( forAttribute ) + { + n.append( " " ); + } + break; + + default: + n.append( c ); + break; + } + } + + if ( forAttribute ) + { + n.append( quoteChar ); + } + } + catch ( IOException e ) + { + throw new RuntimeException( e ); + } + + } + + /** + * Returns string as CDATA block if possible, otherwise null. + */ + public static String xmlEncodeTextAsCDATABlock( String text ) + { + if ( text == null ) + { + return null; + } + if ( isCompatibleWithCDATABlock( text ) ) + { + return ""; + } + else + { + return null; + } + } + + /** + * Checks if this text needs encoding in order to be represented in XML. + */ + public static boolean needsEncoding( String text ) + { + return needsEncoding( text, false ); + } + + /** + * Checks if this text needs encoding in order to be represented in XML. + *

          + * Set checkForAttr if you want to check for storability in + * an attribute. + */ + public static boolean needsEncoding( String data, boolean checkForAttr ) + { + if ( data == null ) + { + return false; + } + char c; + for ( int i = 0; i < data.length(); i++ ) + { + c = data.charAt( i ); + if ( c == '&' || c == '<' || ( checkForAttr && ( c == '"' || c == '\'' ) ) ) + { + return true; + } + } + return false; + } + + /** + * Can this text be stored into a CDATA block? + */ + public static boolean isCompatibleWithCDATABlock( String text ) + { + return text != null && ( !text.contains( "]]>" ) ); + } + + /** + * Make CDATA out of possibly encoded PCDATA.
          + * E.g. make '&' out of '&amp;' + */ + public static String xmlDecodeTextToCDATA( String pcdata ) + { + if ( pcdata == null ) + { + return null; + } + char c, c1, c2, c3, c4, c5; + StringBuilder n = new StringBuilder( pcdata.length() ); + for ( int i = 0; i < pcdata.length(); i++ ) + { + c = pcdata.charAt( i ); + if ( c == '&' ) + { + c1 = lookAhead( 1, i, pcdata ); + c2 = lookAhead( 2, i, pcdata ); + c3 = lookAhead( 3, i, pcdata ); + c4 = lookAhead( 4, i, pcdata ); + c5 = lookAhead( 5, i, pcdata ); + + if ( c1 == 'a' && c2 == 'm' && c3 == 'p' && c4 == ';' ) + { + n.append( "&" ); + i += 4; + } + else if ( c1 == 'l' && c2 == 't' && c3 == ';' ) + { + n.append( "<" ); + i += 3; + } + else if ( c1 == 'g' && c2 == 't' && c3 == ';' ) + { + n.append( ">" ); + i += 3; + } + else if ( c1 == 'q' && c2 == 'u' && c3 == 'o' && c4 == 't' && c5 == ';' ) + { + n.append( "\"" ); + i += 5; + } + else if ( c1 == 'a' && c2 == 'p' && c3 == 'o' && c4 == 's' && c5 == ';' ) + { + n.append( "'" ); + i += 5; + } + else + { + n.append( "&" ); + } + } + else + { + n.append( c ); + } + } + return n.toString(); + } + + private static char lookAhead( int la, int offset, String data ) + { + try + { + return data.charAt( offset + la ); + } + catch ( StringIndexOutOfBoundsException e ) + { + return 0x0; + } + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XMLWriter.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XMLWriter.java new file mode 100644 index 000000000..0daad8bc2 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XMLWriter.java @@ -0,0 +1,90 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; + +/** + * Interface for tools writing XML files. + * XMLWriters are not thread safe and must not be accessed concurrently. + */ +public interface XMLWriter +{ + + /** + * Sets the encoding of the document. + * If not set, UTF-8 is being used + * + * @param encoding the encoding + * @throws IllegalStateException if the generation of the document has already started + */ + void setEncoding( String encoding ); + + /** + * Sets the docType of the document. + * + * @param docType the docType + * @throws IllegalStateException if the generation of the document has already started + */ + void setDocType( String docType ); + + + /** + * Start an XML Element tag. + * @param name The name of the tag. + * @throws IOException if starting the element fails. + */ + void startElement( String name ) throws IOException; + + + /** + * Add a XML attribute to the current XML Element. + * This method must get called immediately after {@link #startElement(String)} + * @param key The key of the attribute. + * @param value The value of the attribute. + * @throws IllegalStateException if no element tag is currently in process + * @throws IOException if adding the attribute fails. + */ + void addAttribute( String key, String value ) throws IOException; + + /** + * Add a value text to the current element tag + * This will perform XML escaping to guarantee valid content + * @param text The text which should be written. + * @throws IllegalStateException if no element tag got started yet + * @throws IOException if writing the text fails. + */ + void writeText( String text ) throws IOException; + + /** + * Add a preformatted markup to the current element tag + * @param text The text which should be written. + * @throws IllegalStateException if no element tag got started yet + * @throws IOException if writing the markup fails. + */ + void writeMarkup( String text ) throws IOException; + + /** + * End the previously opened element. + * @see #startElement(String) + * @throws IOException if ending the element fails. + */ + void endElement() throws IOException; +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlReaderException.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlReaderException.java new file mode 100644 index 000000000..f1bc0c6f9 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlReaderException.java @@ -0,0 +1,167 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.InputStream; + +/** + * The XmlReaderException is thrown by the XmlReader constructors if the charset encoding can not be determined + * according to the XML 1.0 specification and RFC 3023. + *

          + * The exception returns the unconsumed InputStream to allow the application to do an alternate processing with the + * stream. Note that the original InputStream given to the XmlReader cannot be used as that one has been already read. + *

          + * + * @author Alejandro Abdelnur + * @version revision 1.1 taken on 26/06/2007 from Rome (see https://rome.dev.java.net/source/browse/rome/src/java/com/sun/syndication/io/XmlReaderException.java) + */ +class XmlReaderException + extends IOException +{ + /** + * + */ + private static final long serialVersionUID = 5044326391409597950L; + + private final String bomEncoding; + + private final String xmlGuessEncoding; + + private final String xmlEncoding; + + private final String contentTypeMime; + + private final String contentTypeEncoding; + + private final InputStream is; + + /** + * Creates an exception instance if the charset encoding could not be determined. + *

          + * Instances of this exception are thrown by the XmlReader. + *

          + * + * @param msg message describing the reason for the exception. + * @param bomEnc BOM encoding. + * @param xmlGuessEnc XML guess encoding. + * @param xmlEnc XML prolog encoding. + * @param is the unconsumed InputStream. + */ + XmlReaderException( String msg, String bomEnc, String xmlGuessEnc, String xmlEnc, InputStream is ) + { + this( msg, null, null, bomEnc, xmlGuessEnc, xmlEnc, is ); + } + + /** + * Creates an exception instance if the charset encoding could not be determined. + *

          + * Instances of this exception are thrown by the XmlReader. + *

          + * + * @param msg message describing the reason for the exception. + * @param ctMime MIME type in the content-type. + * @param ctEnc encoding in the content-type. + * @param bomEnc BOM encoding. + * @param xmlGuessEnc XML guess encoding. + * @param xmlEnc XML prolog encoding. + * @param is the unconsumed InputStream. + */ + XmlReaderException( String msg, String ctMime, String ctEnc, String bomEnc, String xmlGuessEnc, + String xmlEnc, InputStream is ) + { + super( msg ); + contentTypeMime = ctMime; + contentTypeEncoding = ctEnc; + bomEncoding = bomEnc; + xmlGuessEncoding = xmlGuessEnc; + xmlEncoding = xmlEnc; + this.is = is; + } + + /** + * Returns the BOM encoding found in the InputStream. + *

          + * + * @return the BOM encoding, null if none. + */ + public String getBomEncoding() + { + return bomEncoding; + } + + /** + * Returns the encoding guess based on the first bytes of the InputStream. + *

          + * + * @return the encoding guess, null if it couldn't be guessed. + */ + public String getXmlGuessEncoding() + { + return xmlGuessEncoding; + } + + /** + * Returns the encoding found in the XML prolog of the InputStream. + *

          + * + * @return the encoding of the XML prolog, null if none. + */ + public String getXmlEncoding() + { + return xmlEncoding; + } + + /** + * Returns the MIME type in the content-type used to attempt determining the encoding. + *

          + * + * @return the MIME type in the content-type, null if there was not content-type or the encoding detection did not + * involve HTTP. + */ + public String getContentTypeMime() + { + return contentTypeMime; + } + + /** + * Returns the encoding in the content-type used to attempt determining the encoding. + *

          + * + * @return the encoding in the content-type, null if there was not content-type, no encoding in it or the encoding + * detection did not involve HTTP. + */ + public String getContentTypeEncoding() + { + return contentTypeEncoding; + } + + /** + * Returns the unconsumed InputStream to allow the application to do an alternate encoding detection on the + * InputStream. + *

          + * + * @return the unconsumed InputStream. + */ + public InputStream getInputStream() + { + return is; + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamReader.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamReader.java new file mode 100644 index 000000000..11a577742 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamReader.java @@ -0,0 +1,174 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.net.URL; +import java.net.URLConnection; +import java.util.regex.Pattern; + +/** + * + */ +public class XmlStreamReader + extends Reader +{ + private final org.apache.commons.io.input.XmlStreamReader reader; + + private static String staticDefaultEncoding = null; + + /** + * @param encoding define the default encoding. + */ + public static void setDefaultEncoding( String encoding ) + { + staticDefaultEncoding = encoding; + } + + /** + * @return the default encoding. + */ + public static String getDefaultEncoding() + { + return staticDefaultEncoding; + } + + /** + * @param file The file to create it from. + * @throws IOException in case of an error. + */ + public XmlStreamReader( File file ) + throws IOException + { + this( new FileInputStream( file ) ); + } + + /** + * @param is {@link InputStream} + * @throws IOException in case of an error. + */ + public XmlStreamReader( InputStream is ) + throws IOException + { + this( is, true ); + } + + /** + * @param is {@link InputStream} + * @param lenient yes/no + * @throws IOException in case of an error. + * @throws XmlStreamReaderException in case of an error. + */ + public XmlStreamReader( InputStream is, boolean lenient ) + throws IOException, XmlStreamReaderException + { + reader = new org.apache.commons.io.input.XmlStreamReader( is, lenient, staticDefaultEncoding ); + } + + /** + * @param url {@link URL} + * @throws IOException in case of error. + */ + public XmlStreamReader( URL url ) + throws IOException + { + this( url.openConnection() ); + } + + /** + * @param conn The URL connection {@link URLConnection}. + * @throws IOException in case of error. + */ + public XmlStreamReader( URLConnection conn ) + throws IOException + { + reader = new org.apache.commons.io.input.XmlStreamReader( conn, staticDefaultEncoding ); + } + + /** + * @param is {@link InputStream} + * @param httpContentType content type. + * @throws IOException in case of error. + */ + public XmlStreamReader( InputStream is, String httpContentType ) + throws IOException + { + this( is, httpContentType, true ); + } + + /** + * @param is {@link InputStream} + * @param httpContentType content type. + * @param lenient yes/no. + * @param defaultEncoding The default encoding. + * @throws IOException in case of error. + * @throws XmlStreamReaderException in case of error. + */ + public XmlStreamReader( InputStream is, String httpContentType, boolean lenient, String defaultEncoding ) + throws IOException, XmlStreamReaderException + { + reader = new org.apache.commons.io.input.XmlStreamReader( is, httpContentType, lenient, + ( defaultEncoding == null ) + ? staticDefaultEncoding + : defaultEncoding ); + } + + /** + * @param is {@link InputStream} + * @param httpContentType content type. + * @param lenient yes/no. + * @throws IOException in case of error. + * @throws XmlStreamReaderException in case of error. + */ + public XmlStreamReader( InputStream is, String httpContentType, boolean lenient ) + throws IOException, XmlStreamReaderException + { + this( is, httpContentType, lenient, null ); + } + + /** + * @return The current encoding. + */ + public String getEncoding() + { + return reader.getEncoding(); + } + + /** {@inheritDoc} */ + public int read( char[] buf, int offset, int len ) + throws IOException + { + return reader.read( buf, offset, len ); + } + + /** {@inheritDoc} */ + public void close() + throws IOException + { + reader.close(); + } + + static final Pattern ENCODING_PATTERN = + Pattern.compile( "<\\?xml.*encoding[\\s]*=[\\s]*((?:\".[^\"]*\")|(?:'.[^']*'))", Pattern.MULTILINE ); +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamReaderException.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamReaderException.java new file mode 100644 index 000000000..eeabae533 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamReaderException.java @@ -0,0 +1,81 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.InputStream; + +/** + * The XmlStreamReaderException is thrown by the XmlStreamReader constructors if the charset encoding can not be + * determined according to the XML 1.0 specification and RFC 3023. + *

          + * The exception returns the unconsumed InputStream to allow the application to do an alternate processing with the + * stream. Note that the original InputStream given to the XmlStreamReader cannot be used as that one has been already + * read. + *

          + * + * @author Alejandro Abdelnur + * @version revision 1.1 taken on 26/06/2007 from Rome (see + * https://rome.dev.java.net/source/browse/rome/src/java/com/sun/syndication/io/XmlReaderException.java) + */ +class XmlStreamReaderException + extends XmlReaderException +{ + /** + * + */ + private static final long serialVersionUID = 1007947701939672080L; + + /** + * Creates an exception instance if the charset encoding could not be determined. + *

          + * Instances of this exception are thrown by the XmlReader. + *

          + * + * @param msg message describing the reason for the exception. + * @param bomEnc BOM encoding. + * @param xmlGuessEnc XML guess encoding. + * @param xmlEnc XML prolog encoding. + * @param is the unconsumed InputStream. + */ + XmlStreamReaderException( String msg, String bomEnc, String xmlGuessEnc, String xmlEnc, InputStream is ) + { + super( msg, bomEnc, xmlGuessEnc, xmlEnc, is ); + } + + /** + * Creates an exception instance if the charset encoding could not be determined. + *

          + * Instances of this exception are thrown by the XmlReader. + *

          + * + * @param msg message describing the reason for the exception. + * @param ctMime MIME type in the content-type. + * @param ctEnc encoding in the content-type. + * @param bomEnc BOM encoding. + * @param xmlGuessEnc XML guess encoding. + * @param xmlEnc XML prolog encoding. + * @param is the unconsumed InputStream. + */ + XmlStreamReaderException( String msg, String ctMime, String ctEnc, String bomEnc, String xmlGuessEnc, + String xmlEnc, InputStream is ) + { + super( msg, ctMime, ctEnc, bomEnc, xmlGuessEnc, xmlEnc, is ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamWriter.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamWriter.java new file mode 100644 index 000000000..2923faf59 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlStreamWriter.java @@ -0,0 +1,50 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.OutputStream; + +/** + * We just wrap the commons StreamWriter to not get into troubles + * by exposing shaded commons-io packages + */ +public class XmlStreamWriter + extends org.apache.commons.io.output.XmlStreamWriter +{ + /** + * @param out {@link OutputStream} + */ + public XmlStreamWriter( OutputStream out ) + { + super( out ); + } + + /** + * @param file The file to use. + * @throws FileNotFoundException in case of not found file. + */ + public XmlStreamWriter( File file ) + throws FileNotFoundException + { + super( file ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlWriterUtil.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlWriterUtil.java new file mode 100644 index 000000000..d9dd49e34 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/XmlWriterUtil.java @@ -0,0 +1,362 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import org.apache.maven.shared.utils.StringUtils; + +/** + * Utility class for the XmlWriter class. + * + * @author Vincent Siveton + * + */ +public class XmlWriterUtil +{ + /** The vm line separator */ + public static final String LS = System.getProperty( "line.separator" ); + + /** The default line indenter size i.e. 2. */ + public static final int DEFAULT_INDENTATION_SIZE = 2; + + /** The default column before line wrapping i.e. 80. */ + public static final int DEFAULT_COLUMN_LINE = 80; + + /** + * Convenience method to write one CRLF. + * + * @param writer not null writer + * @throws IOException if writing fails. + */ + public static void writeLineBreak( XMLWriter writer ) throws IOException + { + writeLineBreak( writer, 1 ); + } + + /** + * Convenience method to repeat CRLF. + * + * @param writer not null + * @param repeat positive number + * @throws IOException if writing fails. + */ + public static void writeLineBreak( XMLWriter writer, int repeat ) throws IOException + { + for ( int i = 0; i < repeat; i++ ) + { + writer.writeMarkup( LS ); + } + } + + /** + * Convenience method to repeat CRLF and to indent the writer by 2. + * + * @param writer not null + * @param repeat The number of repetitions of the indent + * @param indent positive number + * @see #DEFAULT_INDENTATION_SIZE + * @see #writeLineBreak(XMLWriter, int, int, int) + * @throws IOException if writing fails. + */ + public static void writeLineBreak( XMLWriter writer, int repeat, int indent ) throws IOException + { + writeLineBreak( writer, repeat, indent, DEFAULT_INDENTATION_SIZE ); + } + + /** + * Convenience method to repeat CRLF and to indent the writer by indentSize. + * + * @param writer not null + * @param repeat The number of repetitions of the indent + * @param indent positive number + * @param indentSize positive number + * @throws IOException if writing fails. + */ + public static void writeLineBreak( XMLWriter writer, int repeat, int indent, int indentSize ) throws IOException + { + writeLineBreak( writer, repeat ); + + if ( indent < 0 ) + { + indent = 0; + } + + if ( indentSize < 0 ) + { + indentSize = 0; + } + + writer.writeText( StringUtils.repeat( " ", indent * indentSize ) ); + } + + /** + * Convenience method to write XML comment line break. Its size is 80. + * + * @param writer not null + * @see #DEFAULT_COLUMN_LINE + * @see #writeCommentLineBreak(XMLWriter, int) + * @throws IOException if writing fails. + */ + public static void writeCommentLineBreak( XMLWriter writer ) throws IOException + { + writeCommentLineBreak( writer, DEFAULT_COLUMN_LINE ); + } + + /** + * Convenience method to write XML comment line break with columnSize as length. + * + * @param writer not null + * @param columnSize positive number + * @throws IOException if writing fails. + */ + public static void writeCommentLineBreak( XMLWriter writer, int columnSize ) throws IOException + { + if ( columnSize < 10 ) + { + columnSize = DEFAULT_COLUMN_LINE; + } + + writer.writeMarkup( "" + LS ); + } + + /** + * Convenience method to write XML comment line. The comment is splitted to have a size of + * 80. + * + * @param writer not null + * @param comment The comment to write + * @see #DEFAULT_INDENTATION_SIZE + * @see #writeComment(XMLWriter, String, int, int) + * @throws IOException if writing fails. + */ + public static void writeComment( XMLWriter writer, String comment ) throws IOException + { + writeComment( writer, comment, 0, DEFAULT_INDENTATION_SIZE ); + } + + /** + * Convenience method to write XML comment line. The comment is split to have a size of + * 80 and is indented by indent using 2 as indentation size. + * + * @param writer not null + * @param comment The comment to write + * @param indent positive number + * @see #DEFAULT_INDENTATION_SIZE + * @see #writeComment(XMLWriter, String, int, int) + * @throws IOException if writing fails. + */ + public static void writeComment( XMLWriter writer, String comment, int indent ) throws IOException + { + writeComment( writer, comment, indent, DEFAULT_INDENTATION_SIZE ); + } + + /** + * Convenience method to write XML comment line. The comment is split to have a size of 80 + * and is indented by indent using indentSize. + * + * @param writer not null + * @param comment The comment to write + * @param indent positive number + * @param indentSize positive number + * @see #DEFAULT_COLUMN_LINE + * @see #writeComment(XMLWriter, String, int, int, int) + * @throws IOException if writing fails. + */ + public static void writeComment( XMLWriter writer, String comment, int indent, int indentSize ) throws IOException + { + writeComment( writer, comment, indent, indentSize, DEFAULT_COLUMN_LINE ); + } + + /** + * Convenience method to write XML comment line. The comment is split to have a size of + * columnSize and is indented by indent using indentSize. + * + * @param writer not null + * @param comment The comment to write + * @param indent positive number + * @param indentSize positive number + * @param columnSize positive number + * @throws IOException if writing fails. + */ + public static void writeComment( XMLWriter writer, String comment, int indent, int indentSize, int columnSize ) + throws IOException + { + if ( comment == null ) + { + comment = "null"; + } + + if ( indent < 0 ) + { + indent = 0; + } + + if ( indentSize < 0 ) + { + indentSize = 0; + } + + if ( columnSize < 0 ) + { + columnSize = DEFAULT_COLUMN_LINE; + } + + String indentation = StringUtils.repeat( " ", indent * indentSize ); + int magicNumber = indentation.length() + columnSize - "-->".length() - 1; + String[] sentences = StringUtils.split( comment, LS ); + + StringBuffer line = new StringBuffer( indentation + "" ).append( LS ); + writer.writeMarkup( line.toString() ); + } + line = new StringBuffer( indentation + "" ).append( LS ); + + writer.writeMarkup( line.toString() ); + } + + /** + * Convenience method to write XML comments between two comments line break. + * The XML comment block is not indented. + * + * @param writer not null + * @param comment The comment to write + * @see #DEFAULT_INDENTATION_SIZE + * @see #writeCommentText(XMLWriter, String, int, int) + * @throws IOException if writing fails. + */ + public static void writeCommentText( XMLWriter writer, String comment ) throws IOException + { + writeCommentText( writer, comment, 0, DEFAULT_INDENTATION_SIZE ); + } + + /** + * Convenience method to write XML comments between two comments line break. + * The XML comment block is also indented by indent using + * 2 as indentation size. + * + * @param writer not null + * @param comment The comment to write + * @param indent positive number + * @see #DEFAULT_INDENTATION_SIZE + * @see #writeCommentText(XMLWriter, String, int, int) + * @throws IOException if writing fails. + */ + public static void writeCommentText( XMLWriter writer, String comment, int indent ) throws IOException + { + writeCommentText( writer, comment, indent, DEFAULT_INDENTATION_SIZE ); + } + + /** + * Convenience method to write XML comment between two comment line break. + * The XML comment block is also indented by indent using indentSize. + * + * @param writer not null + * @param comment The comment to write + * @param indent positive number + * @param indentSize positive number + * @see #DEFAULT_COLUMN_LINE + * @see #writeCommentText(XMLWriter, String, int, int, int) + * @throws IOException if writing fails. + */ + public static void writeCommentText( XMLWriter writer, String comment, int indent, int indentSize ) + throws IOException + { + writeCommentText( writer, comment, indent, indentSize, DEFAULT_COLUMN_LINE ); + } + + /** + * Convenience method to write XML comments between two comments line break. + * The XML comment block is also indented by indent using indentSize. + * The column size could be also be specified. + * + * @param writer not null + * @param comment The comment to write + * @param indent positive number + * @param indentSize positive number + * @param columnSize positive number + * @throws IOException if writing fails. + */ + public static void writeCommentText( XMLWriter writer, String comment, int indent, int indentSize, int columnSize ) + throws IOException + { + if ( indent < 0 ) + { + indent = 0; + } + + if ( indentSize < 0 ) + { + indentSize = 0; + } + + if ( columnSize < 0 ) + { + columnSize = DEFAULT_COLUMN_LINE; + } + + writeLineBreak( writer, 1 ); + + writer.writeMarkup( StringUtils.repeat( " ", indent * indentSize ) ); + writeCommentLineBreak( writer, columnSize ); + + writeComment( writer, comment, indent, indentSize, columnSize ); + + writer.writeMarkup( StringUtils.repeat( " ", indent * indentSize ) ); + writeCommentLineBreak( writer, columnSize ); + + writeLineBreak( writer, 1, indent, indentSize ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java new file mode 100644 index 000000000..1b2f1b66e --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java @@ -0,0 +1,429 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +/** + * A reimplementation of Plexus Xpp3Dom based on the public interface of Plexus Xpp3Dom. + * + * @author Kristian Rosenvold + */ +public class Xpp3Dom + implements Iterable +{ + private static final long serialVersionUID = 2567894443061173996L; + + private String name; // plexus: protected + + private String value; // plexus: protected + + private Map attributes; // plexus: protected + + final List childList; // plexus: protected + + final Map childMap; // plexus: protected + + private Xpp3Dom parent; // plexus: protected + + /** + * The attribute which identifies merge/append. + */ + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; + + private static final String CHILDREN_COMBINATION_MERGE = "merge"; + + /** + * The attribute append. + */ + public static final String CHILDREN_COMBINATION_APPEND = "append"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; // plexus: public + + /** + * The name of the attribute. + */ + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + + /** + * The attributes which identifies override. + */ + public static final String SELF_COMBINATION_OVERRIDE = "override"; // plexus: public + + /** + * The attribute which identifies merge + */ + public static final String SELF_COMBINATION_MERGE = "merge"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; // plexus: public + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0]; + + /** + * @param name The name of the instance. + */ + public Xpp3Dom( String name ) + { + this.name = name; + childList = new ArrayList(); + childMap = new HashMap(); + } + + /** + * Create instance. + * @param source The source. + */ + public Xpp3Dom( Xpp3Dom source ) + { + this( source, source.getName() ); + } + + /** + * Create instance. + * @param src The source Dom. + * @param name The name of the Dom. + */ + public Xpp3Dom( @Nonnull Xpp3Dom src, String name ) + { + this.name = name; + + int size = src.getChildCount(); + childList = new ArrayList( size ); + childMap = new HashMap(); + + setValue( src.getValue() ); + + for ( String attributeName : src.getAttributeNames() ) + { + setAttribute( attributeName, src.getAttribute( attributeName ) ); + } + + for ( Xpp3Dom xpp3Dom : src.getChildren() ) + { + addChild( new Xpp3Dom( xpp3Dom ) ); + } + } + + /** + * @return The current name. + */ + public String getName() + { + return name; + } + + /** + * @return The current value. + */ + @Nonnull public String getValue() + { + return value; + } + + /** + * @param value The value to be set. + */ + public void setValue( @Nonnull String value ) + { + this.value = value; + } + + + /** + * @return The array of attribute names. + */ + public String[] getAttributeNames() + { + boolean isNothing = attributes == null || attributes.isEmpty(); + return isNothing ? EMPTY_STRING_ARRAY : attributes.keySet().toArray( new String[attributes.size()] ); + } + + + /** + * @param nameParameter The name of the attribute. + * @return The attribute value. + */ + public String getAttribute( String nameParameter ) + { + return this.attributes != null ? this.attributes.get( nameParameter ) : null; + } + + /** + * @param nameParameter The name of the attribute. + * @param valueParameter The value of the attribute. + */ + public void setAttribute( @Nonnull String nameParameter, @Nonnull String valueParameter ) + { + if ( valueParameter == null ) + { + throw new NullPointerException( "value can not be null" ); + } + if ( nameParameter == null ) + { + throw new NullPointerException( "name can not be null" ); + } + if ( attributes == null ) + { + attributes = new HashMap(); + } + + attributes.put( nameParameter, valueParameter ); + } + + /** + * @param i The index to be selected. + * @return The child selected by index. + */ + public Xpp3Dom getChild( int i ) + { + return childList.get( i ); + } + + /** + * @param nameParameter The name of the child. + * @return The child selected by name. + */ + public Xpp3Dom getChild( String nameParameter ) + { + return childMap.get( nameParameter ); + } + + /** + * @param child The child to be added. + */ + public void addChild( Xpp3Dom child ) + { + child.setParent( this ); + childList.add( child ); + childMap.put( child.getName(), child ); + } + + /** + * @return The array of childs. + */ + public Xpp3Dom[] getChildren() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? EMPTY_DOM_ARRAY : childList.toArray( new Xpp3Dom[childList.size()] ); + } + + private List getChildrenList() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? Collections.emptyList() : childList; + } + + /** + * @param nameParameter The name of the child. + * @return The array of the Dom. + */ + public Xpp3Dom[] getChildren( String nameParameter ) + { + List children = getChildrenList( nameParameter ); + return children.toArray( new Xpp3Dom[children.size()] ); + } + + List getChildrenList( String nameParameter ) + { + if ( childList == null ) + { + return Collections.emptyList(); + } + else + { + ArrayList children = new ArrayList(); + for ( Xpp3Dom aChildList : childList ) + { + if ( nameParameter.equals( aChildList.getName() ) ) + { + children.add( aChildList ); + } + } + return children; + } + } + + /** + * @return The number of childs. + */ + public int getChildCount() + { + if ( childList == null ) + { + return 0; + } + + return childList.size(); + } + + /** + * @param i The child to be removed. + */ + public void removeChild( int i ) + { + Xpp3Dom child = childList.remove( i ); + childMap.values().remove( child ); + child.setParent( null ); + } + + /** + * @return The current parent. + */ + public Xpp3Dom getParent() + { + return parent; + } + + /** + * @param parent Set the parent. + */ + public void setParent( Xpp3Dom parent ) + { + this.parent = parent; + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @param childMergeOverride true if child merge will take precedence false otherwise. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive, childMergeOverride ); + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive ); + } + + /** {@inheritDoc} */ + public boolean equals( Object obj ) + { + if ( obj == this ) + { + return true; + } + + if ( !( obj instanceof Xpp3Dom ) ) + { + return false; + } + + Xpp3Dom dom = (Xpp3Dom) obj; + + return !( name == null ? dom.name != null : !name.equals( dom.name ) ) + && !( value == null ? dom.value != null : !value.equals( dom.value ) ) + && !( attributes == null ? dom.attributes != null : !attributes.equals( dom.attributes ) ) + && !( childList == null ? dom.childList != null : !childList.equals( dom.childList ) ); + } + + /** {@inheritDoc} */ + public int hashCode() + { + int result = 17; + result = 37 * result + ( name != null ? name.hashCode() : 0 ); + result = 37 * result + ( value != null ? value.hashCode() : 0 ); + result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 ); + result = 37 * result + ( childList != null ? childList.hashCode() : 0 ); + return result; + } + + /** {@inheritDoc} */ + public String toString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + /** + * @return Unescaped string. + */ + public String toUnescapedString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this, false ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + private PrettyPrintXMLWriter getPrettyPrintXMLWriter( StringWriter writer ) + { + return new PrettyPrintXMLWriter( writer, "UTF-8", null ); + } + + /** + * @param str The string to be checked. + * @return true if the string is not empty (length > 0) and not null. + */ + public static boolean isNotEmpty( String str ) + { + return str != null && str.length() > 0; + } + + /** + * @param str The string to be checked. + * @return true if the string is empty or null. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + /** {@inheritDoc} */ + public Iterator iterator() + { + return getChildrenList().iterator(); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomBuilder.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomBuilder.java new file mode 100644 index 000000000..bfa77f370 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomBuilder.java @@ -0,0 +1,295 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.io.IOUtil; +import org.apache.maven.shared.utils.xml.pull.XmlPullParserException; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +import javax.annotation.Nonnull; +import javax.annotation.WillClose; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Kristian Rosenvold + */ +public class Xpp3DomBuilder +{ + private static final boolean DEFAULT_TRIM = true; + + /** + * @param reader {@link Reader} + * @return the built dom. + * @throws XmlPullParserException in case of an error. + */ + public static Xpp3Dom build( @WillClose @Nonnull Reader reader ) + throws XmlPullParserException + { + return build( reader, DEFAULT_TRIM ); + } + + /** + * @param is {@link InputStream} + * @param encoding The encoding. + * @return the built dom. + * @throws XmlPullParserException in case of an error. + */ + public static Xpp3Dom build( @WillClose InputStream is, @Nonnull String encoding ) + throws XmlPullParserException + { + return build( is, encoding, DEFAULT_TRIM ); + } + + /** + * @param is {@link InputStream} + * @param encoding The encoding. + * @param trim true/false. + * @return the built dom. + * @throws XmlPullParserException in case of an error. + */ + public static Xpp3Dom build( @WillClose InputStream is, @Nonnull String encoding, boolean trim ) + throws XmlPullParserException + { + try + { + Reader reader = new InputStreamReader( is, encoding ); + return build( reader, trim ); + } + catch ( UnsupportedEncodingException e ) + { + throw new RuntimeException( e ); + } + } + + /** + * @param reader {@link Reader} + * @param trim true/false + * @return the built dom + * @throws XmlPullParserException in case of an error + */ + public static Xpp3Dom build( @WillClose Reader reader, boolean trim ) + throws XmlPullParserException + { + try + { + DocHandler docHandler = parseSax( new InputSource( reader ), trim ); + reader.close(); + return docHandler.result; + } + catch ( final IOException e ) + { + throw new XmlPullParserException( e ); + } + finally + { + IOUtil.close( reader ); + } + } + + private static DocHandler parseSax( @Nonnull InputSource inputSource, boolean trim ) + throws XmlPullParserException + { + try + { + DocHandler ch = new DocHandler( trim ); + XMLReader parser = createXmlReader(); + parser.setContentHandler( ch ); + parser.parse( inputSource ); + return ch; + } + catch ( IOException e ) + { + throw new XmlPullParserException( e ); + } + catch ( SAXException e ) + { + throw new XmlPullParserException( e ); + } + } + + + private static XMLReader createXmlReader() + throws SAXException + { + XMLReader comSunXmlReader = instantiate( "com.sun.org.apache.xerces.internal.parsers.SAXParser" ); + if ( comSunXmlReader != null ) + { + return comSunXmlReader; + } + + String key = "org.xml.sax.driver"; + String oldParser = System.getProperty( key ); + System.clearProperty( key ); // There's a "slight" problem with this an parallel maven: It does not work ;) + + try + { + return org.xml.sax.helpers.XMLReaderFactory.createXMLReader(); + } + finally + { + if ( oldParser != null ) + { + System.setProperty( key, oldParser ); + } + } + + } + + private static XMLReader instantiate( String s ) + { + try + { + Class aClass = Thread.currentThread().getContextClassLoader().loadClass( s ); + return (XMLReader) aClass.newInstance(); + } + catch ( ClassNotFoundException e ) + { + return null; + } + catch ( InstantiationException e ) + { + return null; + } + catch ( IllegalAccessException e ) + { + return null; + } + } + + + private static class DocHandler + extends DefaultHandler + { + private final List elemStack = new ArrayList(); + + private final List values = new ArrayList(); + + Xpp3Dom result = null; + + private final boolean trim; + + private boolean spacePreserve = false; + + DocHandler( boolean trim ) + { + this.trim = trim; + } + + @Override + public void startElement( String uri, String localName, String qName, Attributes attributes ) + throws SAXException + { + spacePreserve = false; + Xpp3Dom child = new Xpp3Dom( localName ); + + attachToParent( child ); + pushOnStack( child ); + + // Todo: Detecting tags that close immediately seem to be impossible in sax ? + // http://stackoverflow.com/questions/12968390/detecting-self-closing-tags-in-sax + values.add( new StringBuilder() ); + + int size = attributes.getLength(); + for ( int i = 0; i < size; i++ ) + { + String name = attributes.getQName( i ); + String value = attributes.getValue( i ); + child.setAttribute( name, value ); + spacePreserve = spacePreserve || ( "xml:space".equals( name ) && "preserve".equals( value ) ); + } + } + + private boolean pushOnStack( Xpp3Dom child ) + { + return elemStack.add( child ); + } + + private void attachToParent( Xpp3Dom child ) + { + int depth = elemStack.size(); + if ( depth > 0 ) + { + elemStack.get( depth - 1 ).addChild( child ); + } + } + + private Xpp3Dom pop() + { + int depth = elemStack.size() - 1; + return elemStack.remove( depth ); + } + + @Override + public void endElement( String uri, String localName, String qName ) + throws SAXException + { + int depth = elemStack.size() - 1; + + Xpp3Dom element = pop(); + + /* this Object could be null if it is a singleton tag */ + Object accumulatedValue = values.remove( depth ); + + if ( element.getChildCount() == 0 ) + { + if ( accumulatedValue == null ) + { + element.setValue( "" ); // null in xpp3dom, but we don't do that around here + } + else + { + element.setValue( accumulatedValue.toString() ); + } + } + + if ( depth == 0 ) + { + result = element; + } + } + + @Override + public void characters( char[] ch, int start, int length ) + throws SAXException + { + String text = new String( ch, start, length ); + appendToTopValue( ( trim && !spacePreserve ) ? text.trim() : text ); + } + + private void appendToTopValue( String toAppend ) + { + // noinspection MismatchedQueryAndUpdateOfStringBuilder + StringBuilder stringBuilder = values.get( values.size() - 1 ); + stringBuilder.append( toAppend ); + } + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomUtils.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomUtils.java new file mode 100644 index 000000000..1b10dc8c6 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomUtils.java @@ -0,0 +1,162 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * + */ +public class Xpp3DomUtils +{ + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @param childMergeOverride true/false. + * @return Merged dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return dominant != null ? merge( dominant, recessive, childMergeOverride ) : recessive; + } + + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @return Merged dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return dominant != null ? merge( dominant, recessive, null ) : recessive; + } + + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @param childMergeOverride true/false. + * @return Merged dom. + */ + public static Xpp3Dom merge( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + if ( recessive == null || isCombineSelfOverride( dominant ) ) + { + return dominant; + } + + if ( isEmpty( dominant.getValue() ) ) + { + dominant.setValue( recessive.getValue() ); + } + + for ( String attr : recessive.getAttributeNames() ) + { + if ( isEmpty( dominant.getAttribute( attr ) ) ) + { + dominant.setAttribute( attr, recessive.getAttribute( attr ) ); + } + } + + if ( recessive.getChildCount() > 0 ) + { + boolean mergeChildren = isMergeChildren( dominant, childMergeOverride ); + + if ( mergeChildren ) + { + Map> commonChildren = getCommonChildren( dominant, recessive ); + for ( Xpp3Dom recessiveChild : recessive ) + { + Iterator it = commonChildren.get( recessiveChild.getName() ); + if ( it == null ) + { + dominant.addChild( new Xpp3Dom( recessiveChild ) ); + } + else if ( it.hasNext() ) + { + Xpp3Dom dominantChild = it.next(); + merge( dominantChild, recessiveChild, childMergeOverride ); + } + } + } + else + { + Xpp3Dom[] dominantChildren = dominant.getChildren(); + dominant.childList.clear(); + for ( Xpp3Dom child : recessive ) + { + dominant.addChild( new Xpp3Dom( child ) ); + } + + for ( Xpp3Dom aDominantChildren : dominantChildren ) + { + dominant.addChild( aDominantChildren ); + } + } + } + return dominant; + } + + private static Map> getCommonChildren( Xpp3Dom dominant, Xpp3Dom recessive ) + { + Map> commonChildren = new HashMap>(); + + for ( String childName : recessive.childMap.keySet() ) + { + List dominantChildren = dominant.getChildrenList( childName ); + if ( dominantChildren.size() > 0 ) + { + commonChildren.put( childName, dominantChildren.iterator() ); + } + } + return commonChildren; + } + + private static boolean isCombineSelfOverride( Xpp3Dom xpp3Dom ) + { + String selfMergeMode = xpp3Dom.getAttribute( Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE ); + return Xpp3Dom.SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ); + } + + private static boolean isMergeChildren( Xpp3Dom dominant, Boolean override ) + { + return override != null ? override : !isMergeChildren( dominant ); + } + + private static boolean isMergeChildren( Xpp3Dom dominant ) + { + return Xpp3Dom.CHILDREN_COMBINATION_APPEND.equals( + dominant.getAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE ) ); + } + + /** + * @param str The string to be checked. + * @return true in case string is empty false otherwise. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + + + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomWriter.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomWriter.java new file mode 100644 index 000000000..b390c5454 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomWriter.java @@ -0,0 +1,96 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +/** + * @author Brett Porter + */ +public class Xpp3DomWriter +{ + /** + * @param writer {@link Writer} + * @param dom {@link Xpp3Dom} + * @throws IOException if writing fails. + */ + public static void write( Writer writer, Xpp3Dom dom ) throws IOException + { + write( new PrettyPrintXMLWriter( writer ), dom ); + } + + /** + * @param writer {@link PrintWriter} + * @param dom {@link Xpp3Dom} + * @throws IOException if writing fails. + */ + public static void write( PrintWriter writer, Xpp3Dom dom ) throws IOException + { + write( new PrettyPrintXMLWriter( writer ), dom ); + } + + /** + * @param xmlWriter {@link XMLWriter} + * @param dom {@link Xpp3Dom} + * @throws IOException if writing fails. + */ + public static void write( XMLWriter xmlWriter, Xpp3Dom dom ) throws IOException + { + write( xmlWriter, dom, true ); + } + + /** + * @param xmlWriter {@link XMLWriter} + * @param dom {@link Xpp3Dom} + * @param escape true/false. + * @throws IOException if writing fails. + */ + public static void write( XMLWriter xmlWriter, Xpp3Dom dom, boolean escape ) throws IOException + { + xmlWriter.startElement( dom.getName() ); + String[] attributeNames = dom.getAttributeNames(); + for ( String attributeName : attributeNames ) + { + xmlWriter.addAttribute( attributeName, dom.getAttribute( attributeName ) ); + } + Xpp3Dom[] children = dom.getChildren(); + for ( Xpp3Dom aChildren : children ) + { + write( xmlWriter, aChildren, escape ); + } + + String value = dom.getValue(); + if ( value != null ) + { + if ( escape ) + { + xmlWriter.writeText( value ); + } + else + { + xmlWriter.writeMarkup( value ); + } + } + xmlWriter.endElement(); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/pull/XmlPullParserException.java b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/pull/XmlPullParserException.java new file mode 100644 index 000000000..3858a9024 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/java/org/apache/maven/shared/utils/xml/pull/XmlPullParserException.java @@ -0,0 +1,61 @@ +package org.apache.maven.shared.utils.xml.pull; + +/* + * 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. + */ + +import org.xml.sax.SAXException; + +import java.io.IOException; + +/** + * + */ +public class XmlPullParserException + extends RuntimeException +{ + + /** + * + */ + private static final long serialVersionUID = 117075811816936575L; + + /** + * @param e IOException. + */ + public XmlPullParserException( IOException e ) + { + super( e ); + } + + /** + * @param e The exception. + */ + public XmlPullParserException( SAXException e ) + { + super( e ); + } + + /** + * @param message The message. + */ + public XmlPullParserException( String message ) + { + super( message ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/main/resources/META-INF/NOTICE b/Java-base/maven-shared-utils/src/src/main/resources/META-INF/NOTICE new file mode 100644 index 000000000..3f59805ce --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/resources/META-INF/NOTICE @@ -0,0 +1,2 @@ +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). diff --git a/Java-base/maven-shared-utils/src/src/main/resources/org/apache/maven/shared/utils/annotations.xml b/Java-base/maven-shared-utils/src/src/main/resources/org/apache/maven/shared/utils/annotations.xml new file mode 100644 index 000000000..8ca14929a --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/main/resources/org/apache/maven/shared/utils/annotations.xml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-shared-utils/src/src/site/apt/index.apt.vm b/Java-base/maven-shared-utils/src/src/site/apt/index.apt.vm new file mode 100644 index 000000000..419146d8b --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/site/apt/index.apt.vm @@ -0,0 +1,52 @@ + ------ + Introduction + ------ + Kristian Rosenvold + ------ + 2013-07-24 + ------ + + ~~ 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. + + ~~ NOTE: For help with the syntax of this file, see: + ~~ http://maven.apache.org/doxia/references/apt-format.html + + +${project.name} + + This project aims to be a functional replacement for + {{{http://codehaus-plexus.github.io/plexus-utils/}plexus-utils}} in Maven. + + It is not a 100% API compatible replacement though but a replacement : + lots of methods got cleaned up, generics got added and we dropped a lot of unused code. + + Then there are additions, like + {{{./apidocs/org/apache/maven/shared/utils/logging/package-summary.html}styled message API}}. + +Why? + + plexus-utils consisted mostly of code that was forked from various Apache projects. + maven-shared-utils is based on the original from the Apache sources. + +Why not commons? + + We would prefer code to use commons-* where appropriate, but the plexus-utils became + slightly incompatible (different) from the commons over the years, so migrating is not + always a 1:1 operation. Migrating to maven-shared-utils is a 1:1 operation in most cases. + + [] diff --git a/Java-base/maven-shared-utils/src/src/site/resources/download.cgi b/Java-base/maven-shared-utils/src/src/site/resources/download.cgi new file mode 100644 index 000000000..1b178d2e6 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/site/resources/download.cgi @@ -0,0 +1,22 @@ +#!/bin/sh +# +# 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. +# +# Just call the standard mirrors.cgi script. It will use download.html +# as the input template. +exec /www/www.apache.org/dyn/mirrors/mirrors.cgi $* \ No newline at end of file diff --git a/Java-base/maven-shared-utils/src/src/site/site.xml b/Java-base/maven-shared-utils/src/src/site/site.xml new file mode 100644 index 000000000..e607a00bc --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/site/site.xml @@ -0,0 +1,38 @@ + + + + + + + +

          + + + + + + + + + + + + + diff --git a/Java-base/maven-shared-utils/src/src/site/xdoc/download.xml.vm b/Java-base/maven-shared-utils/src/src/site/xdoc/download.xml.vm new file mode 100644 index 000000000..666d997c4 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/site/xdoc/download.xml.vm @@ -0,0 +1,126 @@ + + + + + + + Download ${project.name} Source + + +
          + +

          ${project.name} ${project.version} is distributed in source format. Use a source archive if you intend to build + ${project.name} yourself. Otherwise, simply use the ready-made binary artifacts from central repository.

          + +

          You will be prompted for a mirror - if the file is not found on yours, please be patient, as it may take 24 + hours to reach all mirrors.

          + +

          In order to guard against corrupted downloads/installations, it is highly recommended to + verify the signature + of the release bundles against the public KEYS used by the Apache Maven + developers.

          + +

          ${project.name} is distributed under the Apache License, version 2.0.

          + +

          We strongly encourage our users to configure a Maven repository mirror closer to their location, please read How to Use Mirrors for Repositories.

          + + + + +

          + [if-any logo] + + logo + + [end] + The currently selected mirror is + [preferred]. + If you encounter a problem with this mirror, + please select another mirror. + If all mirrors are failing, there are + backup + mirrors + (at the end of the mirrors list) that should be available. +

          + +
          + Other mirrors: + + +
          + +

          + You may also consult the + complete list of + mirrors. +

          + + + + + +

          This is the current stable version of ${project.name}.

          + + + + + + + + + + + + + + + + + + +
          LinkChecksumSignature
          ${project.name} ${project.version} (Source zip)maven/shared/${project.artifactId}-${project.version}-source-release.zipmaven/shared/${project.artifactId}-${project.version}-source-release.zip.sha512maven/shared/${project.artifactId}-${project.version}-source-release.zip.asc
          +
          + + + +

          Older non-recommended releases can be found on our archive site.

          + +
          +
          + +
          + diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/CaseTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/CaseTest.java new file mode 100644 index 000000000..7eae30283 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/CaseTest.java @@ -0,0 +1,172 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.util.Locale; + +import org.apache.commons.text.StringEscapeUtils; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.ComparisonFailure; +import org.junit.Test; + +/** + * Test case for character case changes, to precisely point the situations when character case comparison doesn't + * give intuitive result, or why one should avoid {@link String#toUpperCase()} and {@link String#toLowerCase()} + * (platform locale dependent, with sometimes unexpected results) + * but prefer {@link String#equalsIgnoreCase(String)} when possible. + * + * @author Hervé Boutemy + * @see Simple Smiles - Xuelei Fan's Blog + */ +public class CaseTest + extends Assert +{ + private final static Locale LOCALE_TURKISH = new Locale( "tr" ); + + /** common ASCII 'i' */ + private final static char DOTTED_i = '\u0069'; + + /** common ASCII 'I' */ + private final static char DOTLESS_I = '\u0049'; + + /** turkish dotless i = ı */ + private final static char DOTLESS_i = '\u0131'; + + /** turkish dotted I = İ */ + private final static char DOTTED_I = '\u0130'; + + /** http://en.wikipedia.org/wiki/Dot_(diacritic) */ + private final static char COMBINING_DOT_ABOVE = '\u0307'; + + private final static Locale SAVED_DEFAULT_LOCALE = Locale.getDefault(); + + @AfterClass + public static void restoreDefaultLocale() + { + Locale.setDefault( SAVED_DEFAULT_LOCALE ); + } + + /** + * test the known case of upper I which doesn't give commonly expected i in Turkish locale, but ı (dotless i). + * @see The infamous Turkish locale bug + */ + @Test + public void testTurkishI() + { + // check common i and I + assertEquals( "common lowercase i should have a dot", 'i', DOTTED_i ); + assertEquals( "common uppercase I should not have a dot", 'I', DOTLESS_I ); + + final String iIıİ = "iIıİ"; + + // check source encoding doesn't wreck havoc */ + assertUnicodeEquals( "misc i directly in (UTF-8) source", iIıİ, "" + DOTTED_i + DOTLESS_I + DOTLESS_i + + DOTTED_I ); + + // check toUpperCase and toLowerCase difference with turkish and english locales + assertUnicodeEquals( "'iIıİ'.toUpperCase('tr')=='İIIİ'", "" + DOTTED_I + DOTLESS_I + DOTLESS_I + DOTTED_I, + iIıİ.toUpperCase( LOCALE_TURKISH ) ); + assertUnicodeEquals( "'iIıİ'.toLowerCase('tr')=='iııi'", "" + DOTTED_i + DOTLESS_i + DOTLESS_i + DOTTED_i, + iIıİ.toLowerCase( LOCALE_TURKISH ) ); + assertUnicodeEquals( "'iIıİ'.toUpperCase('en')=='IIIİ'", "" + DOTLESS_I + DOTLESS_I + DOTLESS_I + DOTTED_I, + iIıİ.toUpperCase( Locale.ENGLISH ) ); + String lower = iIıİ.toLowerCase( Locale.ENGLISH ); // on some platforms, ends with extra COMBINED DOT ABOVE + assertUnicodeEquals( "'iIıİ'.toLowerCase('en')=='iiıi'", "" + DOTTED_i + DOTTED_i + DOTLESS_i + DOTTED_i + + ( lower.length() > 4 ? COMBINING_DOT_ABOVE : "" ), lower ); + + // check equalsIgnoreCase() , which has no locale + for ( int i = 0; i < iIıİ.length(); i++ ) + { + char currentI = iIıİ.charAt( i ); + + StringBuilder sb = new StringBuilder( iIıİ.length() ); + for ( int j = 0; j < iIıİ.length(); j++ ) + { + sb.append( currentI ); + } + String current = sb.toString(); + + assertTrue( "'" + current + "'.equalsIgnoreCase('" + iIıİ + "')", current.equalsIgnoreCase( iIıİ ) ); + } + } + + /** + * Assert equals, and in case the result isn't as expected, display content unicode-escaped. + * @param message + * @param expected + * @param actual + */ + private void assertUnicodeEquals( String message, String expected, String actual ) + { + if ( expected.equals( actual ) ) + { + return; + } + + throw new ComparisonFailure( message, StringEscapeUtils.escapeJava( expected ), + StringEscapeUtils.escapeJava( actual ) ); + } + + /** + * Test case change on all ascii characters with every available locale, to check that turkish i is the only + * exception on these characters. + */ + @Test + public void testAsciiAvailableLocales() + { + final String lower = "abcdefghijklmnopqrstuvwxyz"; + final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + for ( Locale locale : Locale.getAvailableLocales() ) + { + // check that toUpper() == toUpper(default locale) and toLower() = toLower(default locale) + Locale.setDefault( locale ); + assertEquals( lower.toUpperCase(), lower.toUpperCase( locale ) ); + assertEquals( upper.toLowerCase(), upper.toLowerCase( locale ) ); + + // check result + String expectedToUpperCase = upper; + String expectedToLowerCase = lower; + if ( LOCALE_TURKISH.getLanguage().equals( locale.getLanguage() ) || + new Locale( "az" ).getLanguage().equals( locale.getLanguage() ) ) + { + expectedToUpperCase = upper.replace( DOTLESS_I, DOTTED_I ); + expectedToLowerCase = lower.replace( DOTTED_i, DOTLESS_i ); + } + + assertEquals( "'" + lower + "'.toUpperCase('" + locale.toString() + "')", expectedToUpperCase, + lower.toUpperCase( locale ) ); + assertEquals( "'" + upper + "'.toLowerCase('" + locale.toString() + "')", expectedToLowerCase, + upper.toLowerCase( locale ) ); + + // check that toLowerCase on lower and toUpperCase on upper don't cause harm + assertEquals( "'" + lower + "'.toLowerCase('" + locale.toString() + "')", lower, lower.toLowerCase( locale ) ); + assertEquals( "'" + upper + "'.toUpperCase('" + locale.toString() + "')", upper, upper.toUpperCase( locale ) ); + + // check equalsIgnoreCase + assertTrue( "'" + upper + "'.equalsIgnoreCase('" + lower + "')", upper.equalsIgnoreCase( lower ) ); + assertTrue( "'" + upper + "'.equalsIgnoreCase('" + expectedToLowerCase + "')", + upper.equalsIgnoreCase( expectedToLowerCase ) ); + assertTrue( "'" + expectedToUpperCase + "'.equalsIgnoreCase('" + lower + "')", + expectedToUpperCase.equalsIgnoreCase( lower ) ); + } + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/OsTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/OsTest.java new file mode 100644 index 000000000..951c9fc25 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/OsTest.java @@ -0,0 +1,189 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import org.junit.Assert; + +import java.util.Set; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests the 'Os' class which evaluates operation system specific settings. + * + * @author Mark Struberg + */ +public class OsTest +{ + private String origOsName; + private String origOsArch; + private String origOsVersion; + + + @Before + public void setUp() + { + origOsName = System.getProperty( "os.name" ); + origOsArch = System.getProperty( "os.arch" ); + origOsVersion = System.getProperty( "os.version" ); + + // and now set some special settings ;) + System.setProperty( "os.name" , "os/2" ); + System.setProperty( "os.arch" , "i386" ); + System.setProperty( "os.version", "2.1.32" ); + } + + @After + public void tearDown() + { + // set the original OS settings again + System.setProperty( "os.name" , origOsName ); + System.setProperty( "os.arch" , origOsArch ); + System.setProperty( "os.version", origOsVersion ); + } + + @Test + public void testConstructor() + { + Os os = new Os(); + os.eval(); + + Assert.assertTrue( Os.isName( Os.FAMILY_OS2 ) ); + + Assert.assertFalse( Os.isName( Os.FAMILY_DOS ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_MAC ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_NETWARE ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_OPENVMS ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_OS400 ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_TANDEM ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_UNIX ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_WIN9X ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_WINDOWS ) ); + Assert.assertFalse( Os.isName( Os.FAMILY_ZOS ) ); + } + + @Test + public void testFamilyNames() + { + Assert.assertEquals( Os.FAMILY_DOS, "dos" ); + Assert.assertEquals( Os.FAMILY_MAC, "mac" ); + Assert.assertEquals( Os.FAMILY_NETWARE, "netware" ); + Assert.assertEquals( Os.FAMILY_OPENVMS, "openvms" ); + Assert.assertEquals( Os.FAMILY_OS2, "os/2" ); + Assert.assertEquals( Os.FAMILY_OS400, "os/400" ); + Assert.assertEquals( Os.FAMILY_TANDEM, "tandem" ); + Assert.assertEquals( Os.FAMILY_UNIX, "unix" ); + Assert.assertEquals( Os.FAMILY_WIN9X, "win9x" ); + Assert.assertEquals( Os.FAMILY_WINDOWS, "windows" ); + Assert.assertEquals( Os.FAMILY_ZOS, "z/os" ); + } + + @Test + public void testGetValidFamilies() + { + Set osFamilies = Os.getValidFamilies(); + + Assert.assertTrue( "OsFamilies Set size" + , osFamilies.size() >= 11 ); + + Assert.assertTrue( osFamilies.contains( Os.FAMILY_DOS ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_MAC ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_NETWARE ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_OPENVMS ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_OS2 ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_OS400 ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_TANDEM ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_UNIX ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_WIN9X ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_WINDOWS ) ); + Assert.assertTrue( osFamilies.contains( Os.FAMILY_ZOS ) ); + + } + + + @Test + public void testIsArch() + { + assertThat( "Arch is i386" + , Os.isArch( "i386" ) + , is( true ) ); + + assertThat( "Os is not Mac" + , Os.isArch( "x86_64" ) + , is( false ) ); + } + + @Test + public void testIsFamily() + { + assertThat( "Family is os/2" + , Os.isFamily( Os.FAMILY_OS2 ) + , is( true ) ); + + assertThat( "Family is not mac" + , Os.isFamily( Os.FAMILY_MAC ) + , is( false ) ); + } + + @Test + public void testIsName() + { + assertThat( "Name is os/2" + , Os.isName( "os/2" ) + , is( true ) ); + + assertThat( "Name is not Mac OS X" + , Os.isName( "Mac OS X" ) + , is( false ) ); + } + + @Test + public void testIsValidFamily() + { + assertThat( "os/2 isValidFamily" + , Os.isValidFamily( Os.FAMILY_OS2 ) + , is( true ) ); + + assertThat( "iPone != isValidFamily" + , Os.isValidFamily( "iPhone" ) + , is( false ) ); + } + + @Test + public void testIsVersion() + { + assertThat( "isVersion" + , Os.isVersion( "2.1.32" ) + , is( true ) ); + + assertThat( "isVersion" + , Os.isVersion( "2.1" ) + , is( false ) ); + + assertThat( "isVersion" + , Os.isVersion( "4.5" ) + , is( false ) ); + + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/PathToolTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/PathToolTest.java new file mode 100644 index 000000000..f8db74ff0 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/PathToolTest.java @@ -0,0 +1,158 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import org.hamcrest.CoreMatchers; +import org.junit.Assume; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + + +import java.io.File; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + + +/** + * Test the {@link PathTool} class. + * + * @author Mark Struberg + */ +public class PathToolTest +{ + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + // Keep in sync with testGetRelativeFilePath_Windows() + public void testGetRelativeFilePath_NonWindows() + { + Assume.assumeThat( File.separatorChar, is( '/' ) ); + + assertThat( PathTool.getRelativeFilePath( null, null ) + , is( "" ) ); + + assertThat( PathTool.getRelativeFilePath( null, "/usr/local/java/bin" ) + , is( "" ) ); + + assertThat( PathTool.getRelativeFilePath( "/usr/local", null ) + , is( "" ) ); + + assertThat( PathTool.getRelativeFilePath( "/usr/local", "/usr/local/java/bin" ) + , is( "java/bin" ) ); + + assertThat( PathTool.getRelativeFilePath( "/usr/local", "/usr/local/java/bin/" ) + , is( "java/bin/" ) ); + + assertThat( PathTool.getRelativeFilePath( "/usr/local/java/bin", "/usr/local/" ) + , is( "../../" ) ); + + assertThat( PathTool.getRelativeFilePath( "/usr/local/", "/usr/local/java/bin/java.sh" ) + , is( "java/bin/java.sh" ) ); + + assertThat( PathTool.getRelativeFilePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) + , is( "../../../" ) ); + + assertThat( PathTool.getRelativeFilePath( "/usr/local/", "/bin" ) + , is( "../../bin" ) ); + + assertThat( PathTool.getRelativeFilePath( "/bin", "/usr/local/" ) + , is( "../usr/local/" ) ); + } + + @Test + // Keep in sync with testGetRelativeFilePath_NonWindows() + public void testGetRelativeFilePath_Windows() + { + Assume.assumeThat( File.separatorChar, is( '\\' ) ); + + assertThat( PathTool.getRelativeFilePath( null, null ) + , is( "" ) ); + + assertThat( PathTool.getRelativeFilePath( null, "c:\\usr\\local\\java\\bin" ) + , is( "" ) ); + + assertThat( PathTool.getRelativeFilePath( "c:\\usr\\local", null ) + , is( "" ) ); + + assertThat( PathTool.getRelativeFilePath( "c:\\usr\\local", "c:\\usr\\local\\java\\bin" ) + , is( "java\\bin" ) ); + + assertThat( PathTool.getRelativeFilePath( "c:\\usr\\local", "c:\\usr\\local\\java\\bin\\" ) + , is( "java\\bin\\" ) ); + + assertThat( PathTool.getRelativeFilePath( "c:\\usr\\local\\java\\bin", "c:\\usr\\local\\" ) + , is( "..\\..\\" ) ); + + assertThat( PathTool.getRelativeFilePath( "c:\\usr\\local\\", "c:\\usr\\local\\java\\bin\\java.sh" ) + , is( "java\\bin\\java.sh" ) ); + + assertThat( PathTool.getRelativeFilePath( "c:\\usr\\local\\java\\bin\\java.sh", "c:\\usr\\local\\" ) + , is( "..\\..\\..\\" ) ); + + assertThat( PathTool.getRelativeFilePath( "c:\\usr\\local\\", "c:\\bin" ) + , is( "..\\..\\bin" ) ); + + assertThat( PathTool.getRelativeFilePath( "c:\\bin", "c:\\usr\\local\\" ) + , is( "..\\usr\\local\\" ) ); + } + + @Test + public void testGetRelativePath_2parm() + { + assertThat( PathTool.getRelativePath( null, null ) + , is( "" ) ); + + assertThat( PathTool.getRelativePath( null, "/usr/local/java/bin" ) + , is( "" ) ); + + assertThat( PathTool.getRelativePath( "/usr/local/", null ) + , is( "" ) ); + + assertThat( PathTool.getRelativePath( "/usr/local/", "/usr/local/java/bin" ) + , is( ".." ) ); + + assertThat( PathTool.getRelativePath( "/usr/local/", "/usr/local/java/bin/java.sh" ) + , is( "../.." ) ); + + assertThat( PathTool.getRelativePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) + , is( "" ) ); + } + + @Test + public void testUppercaseDrive() + { + assertThat( PathTool.uppercaseDrive( null ) + , CoreMatchers.nullValue() ); + + assertThat( PathTool.uppercaseDrive( "d:" ) + , is( "D:" ) ); + + assertThat( PathTool.uppercaseDrive( "D:" ) + , is( "D:" ) ); + + assertThat( PathTool.uppercaseDrive( "/notadrive" ) + , is( "/notadrive" ) ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/PropertyUtilsTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/PropertyUtilsTest.java new file mode 100644 index 000000000..0c6964154 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/PropertyUtilsTest.java @@ -0,0 +1,195 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.net.URL; +import java.util.Properties; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +public class PropertyUtilsTest +{ + + @Retention( RetentionPolicy.RUNTIME ) + @Target( ElementType.METHOD ) + @interface NeedsTemporaryFolder + { + } + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + @SuppressWarnings( "deprecation" ) + // @ReproducesPlexusBug( "Should return null on error like url and file do" ) + public void loadNullInputStream() + throws Exception + { + assertThat( PropertyUtils.loadProperties( (InputStream) null ), is( new Properties() ) ); + } + + @Test + public void loadOptionalNullInputStream() + throws Exception + { + assertThat( PropertyUtils.loadOptionalProperties( (InputStream) null ), is( new Properties() ) ); + } + + + @Test + public void loadOptionalProperties_ioException() + throws Exception + { + URL url = new URL( "https://nonesuch12344.foo.bar.com" ); + assertThat( PropertyUtils.loadOptionalProperties( url ), is( new Properties() ) ); + } + + @Test + @SuppressWarnings( "deprecation" ) + public void loadNullURL() + throws Exception + { + assertThat( PropertyUtils.loadProperties( (URL) null ), nullValue( Properties.class ) ); + } + + @Test + public void loadOptionalNullURL() + throws Exception + { + assertThat( PropertyUtils.loadOptionalProperties( (URL) null ), is( new Properties() ) ); + } + + @Test + @SuppressWarnings( "deprecation" ) + public void loadNullFile() + throws Exception + { + assertThat( PropertyUtils.loadProperties( (File) null ), nullValue( Properties.class ) ); + } + + @Test + public void loadOptionalNullFile() + throws Exception + { + assertThat( PropertyUtils.loadOptionalProperties( (File) null ), is( new Properties() ) ); + } + + @Test + @SuppressWarnings( "deprecation" ) + public void loadEmptyInputStream() + throws Exception + { + assertThat( PropertyUtils.loadProperties( new ByteArrayInputStream( new byte[ 0 ] ) ), + is( new Properties() ) ); + + assertThat( PropertyUtils.loadOptionalProperties( new ByteArrayInputStream( new byte[ 0 ] ) ), + is( new Properties() ) ); + + } + + @Test + @NeedsTemporaryFolder + @SuppressWarnings( "deprecation" ) + public void loadEmptyFile() + throws Exception + { + assertThat( PropertyUtils.loadProperties( tempFolder.newFile( "empty" ) ), is( new Properties() ) ); + assertThat( PropertyUtils.loadOptionalProperties( tempFolder.newFile( "optional" ) ), is( new Properties() ) ); + } + + @Test + @NeedsTemporaryFolder + @SuppressWarnings( "deprecation" ) + public void loadEmptyURL() + throws Exception + { + assertThat( PropertyUtils.loadProperties( tempFolder.newFile( "empty" ).toURI().toURL() ), + is( new Properties() ) ); + + assertThat( PropertyUtils.loadOptionalProperties( tempFolder.newFile( "optional" ).toURI().toURL() ), + is( new Properties() ) ); + + } + + @Test + @SuppressWarnings( "deprecation" ) + public void loadValidInputStream() throws UnsupportedEncodingException + { + Properties value = new Properties(); + value.setProperty( "a", "b" ); + + assertThat( PropertyUtils.loadProperties( new ByteArrayInputStream( "a=b".getBytes( "ISO-8859-1" ) ) ), + is( value ) ); + + assertThat( PropertyUtils.loadOptionalProperties( new ByteArrayInputStream( "a=b".getBytes( "ISO-8859-1" ) ) ), + is( value ) ); + + } + + @Test + @NeedsTemporaryFolder + @SuppressWarnings( "deprecation" ) + public void loadValidFile() throws IOException + { + File valid = tempFolder.newFile( "valid" ); + Properties value = new Properties(); + value.setProperty( "a", "b" ); + try ( OutputStream out = new FileOutputStream( valid ) ) + { + value.store( out, "a test" ); + assertThat( PropertyUtils.loadProperties( valid ), is( value ) ); + assertThat( PropertyUtils.loadOptionalProperties( valid ), is( value ) ); + } + } + + @Test + @NeedsTemporaryFolder + @SuppressWarnings( "deprecation" ) + public void loadValidURL() throws IOException + { + File valid = tempFolder.newFile( "valid" ); + Properties value = new Properties(); + value.setProperty( "a", "b" ); + try ( OutputStream out = new FileOutputStream( valid ) ) + { + value.store( out, "a test" ); + assertThat( PropertyUtils.loadProperties( valid.toURI().toURL() ), is( value ) ); + assertThat( PropertyUtils.loadOptionalProperties( valid.toURI().toURL() ), is( value ) ); + } + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/StringUtilsTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/StringUtilsTest.java new file mode 100644 index 000000000..505cf508a --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/StringUtilsTest.java @@ -0,0 +1,2080 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Test the {@link StringUtils} class. + * + * @author Mark Struberg + */ +public class StringUtilsTest +{ + + @Test( expected = NullPointerException.class ) + public void testAbbreviate_NPE() + { + assertThat( StringUtils.abbreviate( null, 10 ) + , nullValue() ); + } + + @Test( expected = IllegalArgumentException.class ) + public void testAbbreviate_MinLength() + { + assertThat( StringUtils.abbreviate( "This is a longtext", 3 ) + , is( "T" ) ); + } + + @Test + public void testAbbreviate() + { + assertThat( StringUtils.abbreviate( "This is a longtext", 10 ) + , is( "This is..." ) ); + + assertThat( StringUtils.abbreviate( "This is a longtext", 50 ) + , is( "This is a longtext" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testAbbreviate_Offset_NPE() + { + assertThat( StringUtils.abbreviate( null, 10, 20 ) + , nullValue() ); + } + + @Test( expected = IllegalArgumentException.class ) + public void testAbbreviate_Offset_MinLength() + { + assertThat( StringUtils.abbreviate( "This is a longtext", 10, 3 ) + , is( "T" ) ); + } + + @Test + public void testAbbreviate_Offset() + { + assertThat( StringUtils.abbreviate( "This is a longtext", 5, 10 ) + , is( "...is a..." ) ); + + assertThat( StringUtils.abbreviate( "This is a longtext", 10, 20 ) + , is( "This is a longtext" ) ); + + assertThat( StringUtils.abbreviate( "This is a longtext", 50, 20 ) + , is( "This is a longtext" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testAddAndDeHump_NPE() + { + StringUtils.addAndDeHump( null ); + } + + @Test + public void testAddAndDeHump() + { + assertThat( StringUtils.addAndDeHump( "lalala" ) + , is( "lalala" ) ); + + assertThat( StringUtils.addAndDeHump( "LaLaLa" ) + , is( "la-la-la" ) ); + + assertThat( StringUtils.addAndDeHump( "ALLUPPER" ) + , is( "a-l-l-u-p-p-e-r" ) ); + + } + + @Test + public void testCapitalise() + { + assertThat( StringUtils.capitalise( null ) + , nullValue() ); + + assertThat( StringUtils.capitalise( "startBig" ) + , is( "StartBig" ) ); + } + + @Test + public void testCapitaliseAllWords() + { + assertThat( StringUtils.capitaliseAllWords( null ) + , nullValue() ); + + assertThat( StringUtils.capitaliseAllWords( "start all big" ) + , is( "Start All Big" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testCapitalizeFirstLetter_NPE() + { + assertThat( StringUtils.capitalizeFirstLetter( null ) + , nullValue() ); + } + + @Test + public void testCapitalizeFirstLetter() + { + assertThat( StringUtils.capitalizeFirstLetter( "Dings" ) + , is( "Dings" ) ); + + assertThat( StringUtils.capitalizeFirstLetter( " dings" ) + , is( " dings" ) ); + + assertThat( StringUtils.capitalizeFirstLetter( "start all big" ) + , is( "Start all big" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testCenter_NPE() + { + StringUtils.center( null, 20 ); + } + + @Test + public void testCenter() + { + assertThat( StringUtils.center( "centerMe", 20 ) + , is( " centerMe " ) ); + + assertThat( StringUtils.center( "centerMe", 4 ) + , is( "centerMe" ) ); + + assertThat( StringUtils.center( " centerMe", 20 ) + , is( " centerMe " ) ); + } + + @Test( expected = NullPointerException.class ) + public void testCenter_Delim_NPE() + { + StringUtils.center( null, 20, "*" ); + } + + @Test + public void testCenter_Delim() + { + assertThat( StringUtils.center( "centerMe", 20, "*" ) + , is( "******centerMe******" ) ); + + assertThat( StringUtils.center( "centerMe", 4, "*" ) + , is( "centerMe" ) ); + + assertThat( StringUtils.center( " centerMe", 20, "*" ) + , is( "** centerMe**" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testChomp_NPE() + { + StringUtils.chomp( null ); + } + + @Test + public void testChomp() + { + assertThat( StringUtils.chomp( "dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.chomp( "dings\n" ) + , is( "dings" ) ); + + assertThat( StringUtils.chomp( "dings\nbums" ) + , is( "dings" ) ); + + assertThat( StringUtils.chomp( "dings\nbums\ndongs" ) + , is( "dings\nbums" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testChomp_Delim_NPE() + { + StringUtils.chomp( null, "+" ); + } + + @Test + public void testChomp_Delim() + { + assertThat( StringUtils.chomp( "dings", "+" ) + , is( "dings" ) ); + + assertThat( StringUtils.chomp( "dings+", "+" ) + , is( "dings" ) ); + + assertThat( StringUtils.chomp( "dings+bums", "+" ) + , is( "dings" ) ); + + assertThat( StringUtils.chomp( "dings+bums+dongs", "+" ) + , is( "dings+bums" ) ); + } + + + @Test( expected = NullPointerException.class ) + public void testChompLast_NPE() + { + StringUtils.chompLast( null ); + } + + @Test + public void testChompLast() + { + assertThat( StringUtils.chompLast( "dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.chompLast( "\n" ) + , is( "" ) ); + + assertThat( StringUtils.chompLast( "dings\n" ) + , is( "dings" ) ); + + assertThat( StringUtils.chompLast( "dings\nbums" ) + , is( "dings\nbums" ) ); + + assertThat( StringUtils.chompLast( "dings\nbums\ndongs\n" ) + , is( "dings\nbums\ndongs" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testChompLast_Delim_NPE() + { + StringUtils.chompLast( null, "+" ); + } + + @Test + public void testChompLast_Delim() + { + assertThat( StringUtils.chompLast( "dings", "+" ) + , is( "dings" ) ); + + assertThat( StringUtils.chompLast( "+", "+" ) + , is( "" ) ); + + assertThat( StringUtils.chompLast( "dings+", "+" ) + , is( "dings" ) ); + + assertThat( StringUtils.chompLast( "dings+bums", "+" ) + , is( "dings+bums" ) ); + + assertThat( StringUtils.chompLast( "dings+bums+dongs+", "+" ) + , is( "dings+bums+dongs" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testChop_NPE() + { + StringUtils.chop( null ); + } + + @Test + public void testChop() + { + assertThat( StringUtils.chop( "dings" ) + , is( "ding" ) ); + + assertThat( StringUtils.chop( "x" ) + , is( "" ) ); + + assertThat( StringUtils.chop( "dings\n" ) + , is( "dings" ) ); + + assertThat( StringUtils.chop( "dings\r\n" ) + , is( "dings" ) ); + + assertThat( StringUtils.chop( "dings\n\r" ) + , is( "dings\n" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testChopNewline_NPE() + { + StringUtils.chopNewline( null ); + } + + @Test + public void testChopNewline() + { + assertThat( StringUtils.chopNewline( "dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.chopNewline( "x" ) + , is( "x" ) ); + + + assertThat( StringUtils.chopNewline( "dings\n" ) + , is( "dings" ) ); + + assertThat( StringUtils.chopNewline( "dings\r\n" ) + , is( "dings" ) ); + + assertThat( StringUtils.chopNewline( "dings\n\r" ) + , is( "dings\n\r" ) ); + } + + + @Test + public void testClean() + { + assertThat( StringUtils.clean( null ) + , is( "" ) ); + + assertThat( StringUtils.clean( " " ) + , is( "" ) ); + + assertThat( StringUtils.clean( " c " ) + , is( "c" ) ); + + assertThat( StringUtils.clean( " dings \n " ) + , is( "dings" ) ); + } + + + @Test( expected = NullPointerException.class ) + public void testConcatenate_NPE() + { + StringUtils.concatenate( null ); + } + + @Test + public void testConcatenate() + { + assertThat( StringUtils.concatenate( new String[0] ) + , is( "" ) ); + + assertThat( StringUtils.concatenate( new String[]{ "x" } ) + , is( "x" ) ); + + assertThat( StringUtils.concatenate( new String[]{ "x", "y", "z" } ) + , is( "xyz" ) ); + } + + @Test + public void testContains_String() + { + assertThat( StringUtils.contains( null, null ) + , is( false ) ); + + assertThat( StringUtils.contains( null, "string" ) + , is( false ) ); + + assertThat( StringUtils.contains( "string", null ) + , is( false ) ); + + assertThat( StringUtils.contains( "string", "" ) + , is( true ) ); + + assertThat( StringUtils.contains( "string", "in" ) + , is( true ) ); + + assertThat( StringUtils.contains( "string", "IN" ) + , is( false ) ); + } + + @Test + public void testContains_Char() + { + assertThat( StringUtils.contains( null, 'c' ) + , is( false ) ); + + assertThat( StringUtils.contains( "string", "c" ) + , is( false ) ); + + assertThat( StringUtils.contains( "string", "" ) + , is( true ) ); + + assertThat( StringUtils.contains( "string", "r" ) + , is( true ) ); + + assertThat( StringUtils.contains( "string", "R" ) + , is( false ) ); + + } + + @Test( expected = NullPointerException.class ) + public void testCountMatches_NPE() + { + StringUtils.countMatches( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testCountMatches_NPE2() + { + StringUtils.countMatches( "this is it", null ); + } + + @Test + public void testCountMatches() + { + assertThat( StringUtils.countMatches( null, "is" ) + , is( 0 ) ); + + assertThat( StringUtils.countMatches( "this is it", "is" ) + , is( 2 ) ); + + assertThat( StringUtils.countMatches( "this is it", "notincluded" ) + , is( 0 ) ); + } + + @Test + public void testDefaultString() + { + assertThat( StringUtils.defaultString( null ) + , is( "" ) ); + + assertThat( StringUtils.defaultString( "dings" ) + , is( "dings" ) ); + } + + @Test + public void testDefaultString_defaultValue() + { + assertThat( StringUtils.defaultString( null, "defaultValue" ) + , is( "defaultValue" ) ); + + assertThat( StringUtils.defaultString( "dings", "defaultValue" ) + , is( "dings" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testDeleteWhitespace_NPE() + { + StringUtils.deleteWhitespace( null ); + } + + @Test + public void testDeleteWhitespace() + { + assertThat( StringUtils.deleteWhitespace( " \t \n" ) + , is( "" ) ); + + assertThat( StringUtils.deleteWhitespace( " \t \b \n" ) + , is( "\b" ) ); + + assertThat( StringUtils.deleteWhitespace( "dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.deleteWhitespace( "\n dings \t " ) + , is( "dings" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testDifference_NPE() + { + StringUtils.difference( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testDifference_NPE2() + { + StringUtils.difference( null, "another" ); + } + + @Test( expected = NullPointerException.class ) + public void testDifference_NPE3() + { + StringUtils.difference( "this", null ); + } + + @Test + public void testDifference() + { + assertThat( StringUtils.difference( "this", "another" ) + , is( "another" ) ); + + assertThat( StringUtils.difference( "I am human", "I am a robot" ) + , is( "a robot" ) ); + + assertThat( StringUtils.difference( "I am human", "I AM a robot" ) + , is( "AM a robot" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testDifferenceAt_NPE() + { + StringUtils.differenceAt( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testDifferenceAt_NPE2() + { + StringUtils.differenceAt( "test", null ); + } + + @Test( expected = NullPointerException.class ) + public void testDifferenceAt_NPE3() + { + StringUtils.differenceAt( null, "test" ); + } + + @Test + public void testDifferenceAt() + { + assertThat( StringUtils.differenceAt( "this", "another" ) + , is( 0 ) ); + + assertThat( StringUtils.differenceAt( "I am human", "I am a robot" ) + , is( 5 ) ); + + assertThat( StringUtils.differenceAt( "I am human", "I AM a robot" ) + , is( 2 ) ); + } + + @Test + public void testEndsWithIgnoreCase() + { + assertThat( StringUtils.endsWithIgnoreCase( null, null ) + , is( false ) ); + + assertThat( StringUtils.endsWithIgnoreCase( null, "string" ) + , is( false ) ); + + assertThat( StringUtils.endsWithIgnoreCase( "string", null ) + , is( false ) ); + + assertThat( StringUtils.endsWithIgnoreCase( "string", "ing" ) + , is( true ) ); + + assertThat( StringUtils.endsWithIgnoreCase( "string", "a string" ) + , is( false ) ); + + assertThat( StringUtils.endsWithIgnoreCase( "string", "str" ) + , is( false ) ); + } + + @Test + public void testEquals() + { + assertThat( StringUtils.equals( null, null ) + , is( true ) ); + + assertThat( StringUtils.equals( "x", null ) + , is( false ) ); + + assertThat( StringUtils.equals( null, "x" ) + , is( false ) ); + + assertThat( StringUtils.equals( "X", "x" ) + , is( false ) ); + + assertThat( StringUtils.equals( "dings", "dings" ) + , is( true ) ); + } + + @Test + public void testEqualsIgnoreCase() + { + assertThat( StringUtils.equalsIgnoreCase( null, null ) + , is( true ) ); + + assertThat( StringUtils.equalsIgnoreCase( "x", null ) + , is( false ) ); + + assertThat( StringUtils.equalsIgnoreCase( null, "x" ) + , is( false ) ); + + assertThat( StringUtils.equalsIgnoreCase( "X", "x" ) + , is( true ) ); + + assertThat( StringUtils.equalsIgnoreCase( "dings", "dings" ) + , is( true ) ); + + assertThat( StringUtils.equalsIgnoreCase( "dings", "diNGs" ) + , is( true ) ); + } + + @Test( expected = NullPointerException.class ) + public void testEscape_NPE() + { + StringUtils.escape( null ); + } + + @Test + public void testEscape() + { + assertThat( StringUtils.escape( "dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.escape( "dings\tbums" ) + , is( "dings\\tbums" ) ); + + assertThat( StringUtils.escape( "dings\nbums" ) + , is( "dings\\nbums" ) ); + } + + + @Test + public void testEscape2() + { + assertThat( StringUtils.escape( null, null, '#' ) + , nullValue() ); + + assertThat( StringUtils.escape( "dings", new char[]{ '\t', '\b' }, '+' ) + , is( "dings" ) ); + + assertThat( StringUtils.escape( "dings\tbums", new char[]{ '\t', '\b' }, '+' ) + , is( "dings+\tbums" ) ); + + assertThat( StringUtils.escape( "dings\nbums", new char[]{ '\t', '\b' }, '+' ) + , is( "dings\nbums" ) ); + assertThat( StringUtils.escape( "dings\bbums", new char[]{ '\t', '\b' }, '+' ) + , is( "dings+\bbums" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testGetChomp_NPE1() + { + StringUtils.getChomp( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testGetChomp_NPE2() + { + StringUtils.getChomp( "dings", null ); + } + + @Test( expected = NullPointerException.class ) + public void testGetChomp_NPE3() + { + StringUtils.getChomp( null, "dings" ); + } + + @Test + public void testGetChomp() + { + assertThat( StringUtils.getChomp( "dings-bums", "-" ) + , is( "-bums" ) ); + + assertThat( StringUtils.getChomp( "dings-", "-" ) + , is( "-" ) ); + + assertThat( StringUtils.getChomp( "dingsbums", "-" ) + , is( "" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testGetNestedString_NPE() + { + assertThat( StringUtils.getNestedString( " +dings+ ", null ) + , nullValue() ); + } + + @Test + public void testGetNestedString() + { + assertThat( StringUtils.getNestedString( null, null ) + , nullValue() ); + + assertThat( StringUtils.getNestedString( " +dings+ ", "+" ) + , is( "dings" ) ); + + assertThat( StringUtils.getNestedString( " +dings+ ", "not" ) + , nullValue() ); + } + + @Test( expected = NullPointerException.class ) + public void testGetNestedString2_NPE1() + { + assertThat( StringUtils.getNestedString( " +dings+ ", null, null ) + , nullValue() ); + } + + @Test( expected = NullPointerException.class ) + public void testGetNestedString2_NPE2() + { + assertThat( StringUtils.getNestedString( " +dings+ ", null, "neither" ) + , nullValue() ); + } + + @Test + public void testGetNestedString2() + { + assertThat( StringUtils.getNestedString( null, null, null ) + , nullValue() ); + + assertThat( StringUtils.getNestedString( " +dings+ ", "not", null ) + , nullValue() ); + + assertThat( StringUtils.getNestedString( " +dings- ", "+", "-" ) + , is( "dings" ) ); + + assertThat( StringUtils.getNestedString( " +dings+ ", "not", "neither" ) + , nullValue() ); + } + + @Test( expected = NullPointerException.class ) + public void testGetPrechomp_NPE1() + { + StringUtils.getPrechomp( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testGetPrechomp_NPE2() + { + StringUtils.getPrechomp( null, "bums" ); + } + + @Test + public void testGetPrechomp() + { + assertThat( StringUtils.getPrechomp( "dings bums dongs", "bums" ) + , is( "dings bums" ) ); + + assertThat( StringUtils.getPrechomp( "dings bums dongs", "non" ) + , is( "" ) ); + } + + @Test + public void testIndexOfAny() + { + assertThat( StringUtils.indexOfAny( null, null ) + , is( -1 ) ); + + assertThat( StringUtils.indexOfAny( "dings", null ) + , is( -1 ) ); + + assertThat( StringUtils.indexOfAny( null, new String[]{} ) + , is( -1 ) ); + + assertThat( StringUtils.indexOfAny( "dings bums dongs", new String[]{ "knuff", "bums" } ) + , is( 6 ) ); + } + + @Test( expected = NullPointerException.class ) + public void testInterpolate_NPE() + { + StringUtils.interpolate( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testInterpolate_NPE2() + { + StringUtils.interpolate( "This ${text} will get replaced", null ); + } + + @Test + public void testInterpolate() + { + Map variables = new HashMap(); + assertThat( StringUtils.interpolate( "This ${text} will get replaced", variables ) + , is( "This ${text} will get replaced" ) ); + + variables.put( "text", "with a special content" ); + + assertThat( StringUtils.interpolate( "This ${text} will get replaced", variables ) + , is( "This with a special content will get replaced" ) ); + } + + @Test + public void testIsAlpha() + { + assertThat( StringUtils.isAlpha( null ) + , is( false ) ); + + assertThat( StringUtils.isAlpha( "2" ) + , is( false ) ); + + assertThat( StringUtils.isAlpha( "asvsdfSDF" ) + , is( true ) ); + + assertThat( StringUtils.isAlpha( "asvsdfSDF \t " ) + , is( false ) ); + + assertThat( StringUtils.isAlpha( "435afsafd3!" ) + , is( false ) ); + } + + @Test + public void testIsAlphaSpace() + { + assertThat( StringUtils.isAlphaSpace( null ) + , is( false ) ); + + assertThat( StringUtils.isAlphaSpace( "2" ) + , is( false ) ); + + assertThat( StringUtils.isAlphaSpace( "asvsdfSDF" ) + , is( true ) ); + + assertThat( StringUtils.isAlphaSpace( "asvsdfSDF " ) + , is( true ) ); + + assertThat( StringUtils.isAlphaSpace( "asvsdfSDF \t " ) + , is( false ) ); + + assertThat( StringUtils.isAlphaSpace( "435afsafd3!" ) + , is( false ) ); + } + + @Test + public void testIsAlphanumeric() + { + assertThat( StringUtils.isAlphanumeric( null ) + , is( false ) ); + + assertThat( StringUtils.isAlphanumeric( "2" ) + , is( true ) ); + + assertThat( StringUtils.isAlphanumeric( "asvsdfSDF" ) + , is( true ) ); + + assertThat( StringUtils.isAlphanumeric( "asvsdfSDF " ) + , is( false ) ); + + assertThat( StringUtils.isAlphanumeric( "asvsdfSDF \t " ) + , is( false ) ); + + assertThat( StringUtils.isAlphanumeric( "435afsafd3!" ) + , is( false ) ); + + assertThat( StringUtils.isAlphanumeric( "435afsafd3" ) + , is( true ) ); + + assertThat( StringUtils.isAlphanumeric( "435 " ) + , is( false ) ); + + assertThat( StringUtils.isAlphanumeric( "435" ) + , is( true ) ); + } + + @Test + public void testIsAlphanumericSpace() + { + assertThat( StringUtils.isAlphanumericSpace( null ) + , is( false ) ); + + assertThat( StringUtils.isAlphanumericSpace( "2" ) + , is( true ) ); + + assertThat( StringUtils.isAlphanumericSpace( "asvsdfSDF" ) + , is( true ) ); + + assertThat( StringUtils.isAlphanumericSpace( "asvsdfSDF " ) + , is( true ) ); + + assertThat( StringUtils.isAlphanumericSpace( "asvsdfSDF \t " ) + , is( false ) ); + + assertThat( StringUtils.isAlphanumericSpace( "435afsafd3!" ) + , is( false ) ); + + assertThat( StringUtils.isAlphanumericSpace( "435afsafd3" ) + , is( true ) ); + + assertThat( StringUtils.isAlphanumericSpace( "435 " ) + , is( true ) ); + + assertThat( StringUtils.isAlphanumericSpace( "435" ) + , is( true ) ); + } + + @Test + public void testIsBlank() + { + assertThat( StringUtils.isBlank( null ) + , is( true ) ); + + assertThat( StringUtils.isBlank( "xx" ) + , is( false ) ); + + assertThat( StringUtils.isBlank( "xx " ) + , is( false ) ); + + assertThat( StringUtils.isBlank( " " ) + , is( true ) ); + + assertThat( StringUtils.isBlank( " \t " ) + , is( true ) ); + + assertThat( StringUtils.isBlank( " \n " ) + , is( true ) ); + } + + + @Test + public void testEmpty() + { + assertThat( StringUtils.isEmpty( null ) + , is( true ) ); + + assertThat( StringUtils.isEmpty( "xx" ) + , is( false ) ); + + assertThat( StringUtils.isEmpty( "xx " ) + , is( false ) ); + + assertThat( StringUtils.isEmpty( " " ) + , is( true ) ); + + assertThat( StringUtils.isEmpty( " \t " ) + , is( true ) ); + + assertThat( StringUtils.isEmpty( " \n " ) + , is( true ) ); + } + + @Test + public void testNotBlank() + { + assertThat( StringUtils.isNotBlank( null ) + , is( false ) ); + + assertThat( StringUtils.isNotBlank( "xx" ) + , is( true ) ); + + assertThat( StringUtils.isNotBlank( "xx " ) + , is( true ) ); + + assertThat( StringUtils.isNotBlank( " " ) + , is( false ) ); + + assertThat( StringUtils.isNotBlank( " \t " ) + , is( false ) ); + + assertThat( StringUtils.isNotBlank( " \n " ) + , is( false ) ); + } + + @Test + public void testNotEmpty() + { + assertThat( StringUtils.isNotEmpty( null ) + , is( false ) ); + + assertThat( StringUtils.isNotEmpty( "xx" ) + , is( true ) ); + + assertThat( StringUtils.isNotEmpty( "xx " ) + , is( true ) ); + + assertThat( StringUtils.isNotEmpty( " " ) + , is( true ) ); + + assertThat( StringUtils.isNotEmpty( "" ) + , is( false ) ); + + assertThat( StringUtils.isNotEmpty( " \t " ) + , is( true ) ); + + assertThat( StringUtils.isNotEmpty( " \n " ) + , is( true ) ); + } + + @Test + public void testIsNumeric() + { + assertThat( StringUtils.isNumeric( null ) + , is( false ) ); + + assertThat( StringUtils.isNumeric( "2" ) + , is( true ) ); + + assertThat( StringUtils.isNumeric( "asvsdfSDF" ) + , is( false ) ); + + assertThat( StringUtils.isNumeric( "asvsdfSDF " ) + , is( false ) ); + + assertThat( StringUtils.isNumeric( "asvsdfSDF \t " ) + , is( false ) ); + + assertThat( StringUtils.isNumeric( "435afsafd3!" ) + , is( false ) ); + + assertThat( StringUtils.isNumeric( "435afsafd3" ) + , is( false ) ); + + assertThat( StringUtils.isNumeric( "435 " ) + , is( false ) ); + + assertThat( StringUtils.isNumeric( "435" ) + , is( true ) ); + } + + @Test + public void testIsWhitespace() + { + assertThat( StringUtils.isWhitespace( null ) + , is( false ) ); + + assertThat( StringUtils.isWhitespace( "xx" ) + , is( false ) ); + + assertThat( StringUtils.isWhitespace( "xx " ) + , is( false ) ); + + assertThat( StringUtils.isWhitespace( " " ) + , is( true ) ); + + assertThat( StringUtils.isWhitespace( "" ) + , is( true ) ); + + assertThat( StringUtils.isWhitespace( " \t " ) + , is( true ) ); + + assertThat( StringUtils.isWhitespace( " \n " ) + , is( true ) ); + } + + @Test( expected = NullPointerException.class ) + public void testJoin_Array_NPE() + { + StringUtils.join( ( Object[] ) null, null ); + } + + @Test + public void testJoin_Array() + { + assertThat( StringUtils.join( new Object[0], null ) + , is( "" ) ); + + assertThat( StringUtils.join( new Object[]{ "a", "b", "c" }, null ) + , is( "abc" ) ); + + assertThat( StringUtils.join( new Object[]{ "a", "b", "c" }, "__" ) + , is( "a__b__c" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testJoin_Iterator_NPE() + { + StringUtils.join( (Iterator) null, null ); + } + + @Test + public void testJoin_Iterator() + { + ArrayList list = new ArrayList(); + + assertThat( StringUtils.join( list.iterator(), null ) + , is( "" ) ); + + list.add( "a" ); + list.add( "b" ); + list.add( "c" ); + + assertThat( StringUtils.join( list.iterator(), null ) + , is( "abc" ) ); + + assertThat( StringUtils.join( list.iterator(), "__" ) + , is( "a__b__c" ) ); + } + + @Test + public void testLastIndexOfAny() + { + assertThat( StringUtils.lastIndexOfAny( null, null ) + , is( -1 ) ); + + assertThat( StringUtils.lastIndexOfAny( "dings", null ) + , is( -1 ) ); + + assertThat( StringUtils.lastIndexOfAny( "dings bums boms", new String[] { "ms", " b" } ) + , is( 13 ) ); + + assertThat( StringUtils.lastIndexOfAny( "dings bums boms", new String[] { "nix", "da" } ) + , is( -1 ) ); + } + + @Test( expected = IllegalArgumentException.class ) + public void testLeft_IAE() + { + StringUtils.left( null, -1 ); + } + + @Test + public void testLeft() + { + assertThat( StringUtils.left( null, 4 ) + , nullValue() ); + + assertThat( StringUtils.left( "dingsbums", 4 ) + , is( "ding" ) ); + + assertThat( StringUtils.left( "dingsbums", 40 ) + , is( "dingsbums" ) ); + + assertThat( StringUtils.left( "dingsbums", 0 ) + , is( "" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testLeftPad1_NPE() + { + StringUtils.leftPad( null, 0 ); + } + + @Test + public void testLeftPad1() + { + assertThat( StringUtils.leftPad( "dings", 0 ) + , is( "dings") ); + + assertThat( StringUtils.leftPad( "dings", 2 ) + , is( "dings") ); + + assertThat( StringUtils.leftPad( "dings", 10 ) + , is( " dings") ); + } + + @Test( expected = NullPointerException.class ) + public void testLeftPad2_NPE1() + { + StringUtils.leftPad( null, 0, null ); + } + + @Test( expected = NullPointerException.class ) + public void testLeftPad2_NPE2() + { + StringUtils.leftPad( "dings", 0, null ); + } + + @Test( expected = NullPointerException.class ) + public void testLeftPad2_NPE3() + { + StringUtils.leftPad( null, 0, "*" ); + } + + @Test + public void testLeftPad2() + { + assertThat( StringUtils.leftPad( "dings", 0, "*" ) + , is( "dings") ); + + assertThat( StringUtils.leftPad( "dings", 2, "*" ) + , is( "dings") ); + + assertThat( StringUtils.leftPad( "dings", 10, "*" ) + , is( "*****dings") ); + } + + @Test + public void testLowerCase() + { + assertThat( StringUtils.lowerCase( null ) + , nullValue() ); + + assertThat( StringUtils.lowerCase( "dinGSbuMS" ) + , is( "dingsbums" ) ); + + assertThat( StringUtils.lowerCase( "" ) + , is( "" ) ); + + } + + @Test( expected = NullPointerException.class ) + public void testLowerCaseFirstLetter_NPE() + { + StringUtils.lowercaseFirstLetter( null ); + } + + @Test + public void testLowerCaseFirstLetter() + { + assertThat( StringUtils.lowercaseFirstLetter( "Dings Bums" ) + , is( "dings Bums" ) ); + } + + @Test( expected = IllegalArgumentException.class ) + public void testMid_NegativeLen() + { + StringUtils.mid( null, 0, -2 ); + } + + @Test( expected = IndexOutOfBoundsException.class ) + public void testMid_WrongPos() + { + StringUtils.mid( null, -2, 3 ); + } + + @Test + public void testMid() + { + assertThat( StringUtils.mid( null, 0, 0 ) + , nullValue() ); + + assertThat( StringUtils.mid( "dings bums", 0, 0 ) + , is( "" ) ); + + assertThat( StringUtils.mid( "dings bums", 3, 4 ) + , is( "gs b" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testOverlayString_NPE1() + { + StringUtils.overlayString( null, null, 0, 0 ); + } + + @Test( expected = NullPointerException.class ) + public void testOverlayString_NPE2() + { + StringUtils.overlayString( "dings", null, 0, 0 ); + } + + @Test( expected = NullPointerException.class ) + public void testOverlayString_NPE3() + { + StringUtils.overlayString( null, "bums", 0, 0 ); + } + + @Test + public void testOverlayString() + { + assertThat( StringUtils.overlayString( "dings", "bums", 0, 0 ) + , is( "bumsdings" ) ); + + assertThat( StringUtils.overlayString( "dings", "bums", 2, 4 ) + , is( "dibumss" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testPrechomp_NPE1() + { + StringUtils.prechomp( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testPrechomp_NPE2() + { + StringUtils.prechomp( "dings", null ); + } + + @Test( expected = NullPointerException.class ) + public void testPrechomp_NPE3() + { + StringUtils.prechomp( null, "bums" ); + } + + @Test + public void testPrechomp() + { + assertThat( StringUtils.prechomp( "dings bums", " " ) + , is( "bums" ) ); + + assertThat( StringUtils.prechomp( "dings bums", "nix" ) + , is( "dings bums" ) ); + } + + @Test + public void testQuoteAndEscape1() + { + assertThat( StringUtils.quoteAndEscape( null, '+' ) + , nullValue() ); + + assertThat( StringUtils.quoteAndEscape( "", '+' ) + , is( "" ) ); + + assertThat( StringUtils.quoteAndEscape( "abc", '"' ) + , is( "abc" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '"' ) + , is( "\"a\\\"bc\"" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\'bc", '\'' ) + , is( "\'a\\'bc\'" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'' ) + , is( "a\"bc" ) ); + } + + @Test + public void testQuoteAndEscape2() + { + assertThat( StringUtils.quoteAndEscape( null, '+', new char[]{ '"' } ) + , nullValue() ); + + assertThat( StringUtils.quoteAndEscape( "", '+', new char[]{ '"' } ) + , is( "" ) ); + + assertThat( StringUtils.quoteAndEscape( "abc", '"', new char[]{ '"' } ) + , is( "abc" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '"', new char[]{ '"' } ) + , is( "\"a\\\"bc\"" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\'bc", '\'', new char[]{ '"' } ) + , is( "\'a\\'bc\'" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'' } ) + , is( "a\"bc" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'', '"' } ) + , is( "\'a\"bc\'" ) ); + } + + @Test + public void testQuoteAndEscape3() + { + assertThat( StringUtils.quoteAndEscape( null, '+', new char[]{ '"' }, '\\', false ) + , nullValue() ); + + assertThat( StringUtils.quoteAndEscape( "", '+', new char[]{ '"' }, '\\', false ) + , is( "" ) ); + + assertThat( StringUtils.quoteAndEscape( "abc", '"', new char[]{ '"' }, '\\', false ) + , is( "abc" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '"', new char[]{ '"' }, '\\', false ) + , is( "\"a\\\"bc\"" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\'bc", '\'', new char[]{ '"' }, '\\', false ) + , is( "a\'bc" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'' }, '\\', false ) + , is( "a\"bc" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'', '"' }, '\\', false ) + , is( "\'a\\\"bc\'" ) ); + + // with force flag + assertThat( StringUtils.quoteAndEscape( null, '+', new char[]{ '"' }, '\\', true ) + , nullValue() ); + + assertThat( StringUtils.quoteAndEscape( "", '+', new char[]{ '"' }, '\\', true ) + , is( "++" ) ); + + assertThat( StringUtils.quoteAndEscape( "abc", '"', new char[]{ '"' }, '\\', true ) + , is( "\"abc\"" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '"', new char[]{ '"' }, '\\', true ) + , is( "\"a\\\"bc\"" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\'bc", '\'', new char[]{ '"' }, '\\', true ) + , is( "\'a\'bc\'" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'' }, '\\', true ) + , is( "\'a\"bc\'" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'', '"' }, '\\', true ) + , is( "\'a\\\"bc\'" ) ); + } + + @Test + public void testQuoteAndEscape4() + { + assertThat( StringUtils.quoteAndEscape( null, '+', new char[]{ '"' }, new char[]{ '"' }, '\\', false ) + , nullValue() ); + + assertThat( StringUtils.quoteAndEscape( "", '+', new char[]{ '"' }, new char[]{ '"' }, '\\', false ) + , is( "" ) ); + + assertThat( StringUtils.quoteAndEscape( "abc", '"', new char[]{ '"' }, new char[]{ '"' }, '\\', false ) + , is( "abc" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '"', new char[]{ '"' }, new char[]{ '"' }, '\\', false ) + , is( "\"a\\\"bc\"" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\'bc", '\'', new char[]{ '"' }, new char[]{ '"' }, '\\', false ) + , is( "a\'bc" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'' }, new char[]{ '"' }, '\\', false ) + , is( "\'a\"bc\'" ) ); + + assertThat( StringUtils.quoteAndEscape( "\'a\"bc\'", '\'', new char[]{ '\'', '"' }, new char[]{ '"' }, '\\', false ) + , is( "\'a\"bc\'" ) ); + + // with force flag + assertThat( StringUtils.quoteAndEscape( null, '+', new char[]{ '"' }, new char[]{ '"' }, '\\', true ) + , nullValue() ); + + assertThat( StringUtils.quoteAndEscape( "", '+', new char[]{ '"' }, new char[]{ '"' }, '\\', true ) + , is( "++" ) ); + + assertThat( StringUtils.quoteAndEscape( "abc", '"', new char[]{ '"' }, new char[]{ '"' }, '\\', true ) + , is( "\"abc\"" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '"', new char[]{ '"' }, new char[]{ '"' }, '\\', true ) + , is( "\"a\\\"bc\"" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\'bc", '\'', new char[]{ '"' }, new char[]{ '"' }, '\\', true ) + , is( "\'a\'bc\'" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'' }, new char[]{ '"' }, '\\', true ) + , is( "\'a\"bc\'" ) ); + + assertThat( StringUtils.quoteAndEscape( "a\"bc", '\'', new char[]{ '\'', '"' }, new char[]{ '"' }, '\\', true ) + , is( "\'a\\\"bc\'" ) ); + } + + @Test( expected = NullPointerException.class ) + public void testRemoveAndHump_NPE1() + { + StringUtils.removeAndHump( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testRemoveAndHump_NPE2() + { + StringUtils.removeAndHump( "dings", null ); + } + + @Test( expected = NullPointerException.class ) + public void testRemoveAndHump_NPE3() + { + StringUtils.removeAndHump( null, "bums" ); + } + + @Test + public void testRemoveAndHump() + { + assertThat( StringUtils.removeAndHump( "dings", "bums" ) + , is( "Ding" ) ); + + assertThat( StringUtils.removeAndHump( "this-is-it", "-" ) + , is( "ThisIsIt" ) ); + + assertThat( StringUtils.removeAndHump( "THIS-IS-IT", "-" ) + , is( "THISISIT" ) ); + + } + + @Test( expected = NullPointerException.class ) + public void testRemoveDuplicateWhitespace_NPE() + { + StringUtils.removeDuplicateWhitespace( null ); + } + + @Test + public void testRemoveDuplicateWhitespace() + { + assertThat( StringUtils.removeDuplicateWhitespace( "dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.removeDuplicateWhitespace( "dings bums" ) + , is( "dings bums" ) ); + + assertThat( StringUtils.removeDuplicateWhitespace( "dings bums" ) + , is( "dings bums" ) ); + + assertThat( StringUtils.removeDuplicateWhitespace( "dings \t bums" ) + , is( "dings bums" ) ); + + } + + @Test( expected = NullPointerException.class ) + public void testRepeat_NPE() + { + StringUtils.repeat( null, 0 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void testRepeat_NegativeAmount() + { + StringUtils.repeat( "dings", -1 ); + } + + + @Test + public void testRepeat() + { + assertThat( StringUtils.repeat( "dings", 0 ) + , is( "" ) ); + + assertThat( StringUtils.repeat( "dings", 1 ) + , is( "dings" ) ); + + assertThat( StringUtils.repeat( "dings", 3 ) + , is( "dingsdingsdings" ) ); + } + + + @Test + public void testReplace_char() + { + assertThat( StringUtils.replace( null, 'i', 'o' ) + , nullValue() ); + + assertThat( StringUtils.replace( "dings", 'i', 'o' ) + , is( "dongs" ) ); + + assertThat( StringUtils.replace( "dingsbims", 'i', 'o' ) + , is( "dongsboms" ) ); + + assertThat( StringUtils.replace( "dings", 'x', 'o' ) + , is( "dings" ) ); + } + + @Test + public void testReplace2_char_max() + { + assertThat( StringUtils.replace( null, 'i', 'o', 0 ) + , nullValue() ); + + assertThat( StringUtils.replace( "dingsibumsi", 'i', 'o', 3 ) + , is( "dongsobumso" ) ); + + assertThat( StringUtils.replace( "dingsibumsi", 'i', 'o', 2 ) + , is( "dongsobumsi" ) ); + + assertThat( StringUtils.replace( "dingsibumsi", 'i', 'o', 0 ) + , is( "dongsobumso" ) ); + + assertThat( StringUtils.replace( "dingsibumsi", 'i', 'o', -2 ) + , is( "dongsobumso" ) ); + + assertThat( StringUtils.replace( "dings", 'x', 'o', 2 ) + , is( "dings" ) ); + } + + @Test + public void testReplace_string() + { + assertThat( StringUtils.replace( null, "in", "ox" ) + , nullValue() ); + + assertThat( StringUtils.replace( "dings", "in", "ox" ) + , is( "doxgs" ) ); + + assertThat( StringUtils.replace( "dingsbins", "in", "ox" ) + , is( "doxgsboxs" ) ); + + assertThat( StringUtils.replace( "dings", "nin", "ox" ) + , is( "dings" ) ); + } + + + @Test + public void testReplace2_string_max() + { + assertThat( StringUtils.replace( null, "in", "ox", 0 ) + , nullValue() ); + + assertThat( StringUtils.replace( "dingsibumsi", "si", "xo", 3 ) + , is( "dingxobumxo" ) ); + + assertThat( StringUtils.replace( "dingsibumsi", "si", "xo", 2 ) + , is( "dingxobumxo" ) ); + + assertThat( StringUtils.replace( "dingsibumsi", "si", "xo", 1 ) + , is( "dingxobumsi" ) ); + + assertThat( StringUtils.replace( "dingsibumsi", "si", "xo", 0 ) + , is( "dingxobumxo" ) ); + + assertThat( StringUtils.replace( "dingsibumsi", "si", "xo", -2 ) + , is( "dingxobumxo" ) ); + + assertThat( StringUtils.replace( "dings", "si", "xo", 2 ) + , is( "dings" ) ); + } + + @Test + public void testReplaceOnce_char() + { + assertThat( StringUtils.replaceOnce( null, 'i', 'o' ) + , nullValue() ); + + assertThat( StringUtils.replaceOnce( "dingsibumsi", 'i', 'o' ) + , is( "dongsibumsi" ) ); + + assertThat( StringUtils.replaceOnce( "dings", 'x', 'o' ) + , is( "dings" ) ); + } + + @Test + public void testReplaceOnce_string() + { + assertThat( StringUtils.replaceOnce( null, "in", "ox" ) + , nullValue() ); + + assertThat( StringUtils.replaceOnce( "dingsibumsi", "si", "xo" ) + , is( "dingxobumsi" ) ); + + assertThat( StringUtils.replaceOnce( "dings", "si", "xo" ) + , is( "dings" ) ); + } + + + @Test + public void testReverse() + { + assertThat( StringUtils.reverse( null ) + , nullValue() ); + + assertThat( StringUtils.reverse( "" ) + , is( "" ) ); + + assertThat( StringUtils.reverse( "dings" ) + , is( "sgnid" ) ); + + assertThat( StringUtils.reverse( " dings " ) + , is( " sgnid " ) ); + } + + @Test( expected = NullPointerException.class ) + public void testReverseDelimitedString_NPE1() + { + StringUtils.reverseDelimitedString( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testReverseDelimitedString_NPE2() + { + StringUtils.reverseDelimitedString( null, " " ); + } + + @Test + public void testReverseDelimitedString() + { + assertThat( StringUtils.reverseDelimitedString( "dings", null ) + , is( "dings" ) ); + + assertThat( StringUtils.reverseDelimitedString( "", " " ) + , is( "" ) ); + + assertThat( StringUtils.reverseDelimitedString( "dings", " " ) + , is( "dings" ) ); + + assertThat( StringUtils.reverseDelimitedString( " dings ", " " ) + , is( "dings" ) ); + + assertThat( StringUtils.reverseDelimitedString( "dings bums", " " ) + , is( "bums dings" ) ); + } + + @Test( expected = IllegalArgumentException.class ) + public void testRight_IAE1() + { + StringUtils.right( null, -1 ); + } + + @Test( expected = IllegalArgumentException.class ) + public void testRight_IAE2() + { + StringUtils.right( "dings", -1 ); + } + + @Test + public void testRight() + { + assertThat( StringUtils.right( null, 0 ) + , nullValue() ); + + assertThat( StringUtils.right( "dings", 0 ) + , is( "" ) ); + + assertThat( StringUtils.right( "dings", 3 ) + , is( "ngs" ) ); + + assertThat( StringUtils.right( "dings ", 3 ) + , is( "gs " ) ); + } + + @Test( expected = NullPointerException.class ) + public void testRightPad1_NPE() + { + StringUtils.rightPad( null, 0 ); + } + + @Test + public void testRightPad1() + { + assertThat( StringUtils.rightPad( "dings", 0 ) + , is( "dings" ) ); + + assertThat( StringUtils.rightPad( "dings", 3 ) + , is( "dings" ) ); + + assertThat( StringUtils.rightPad( "dings", 10 ) + , is( "dings " ) ); + } + + @Test( expected = NullPointerException.class ) + public void testRightPad2_NPE1() + { + StringUtils.rightPad( null, 0, null ); + } + + @Test( expected = NullPointerException.class ) + public void testRightPad2_NPE2() + { + StringUtils.rightPad( "dings", 0, null ); + } + + @Test( expected = NullPointerException.class ) + public void testRightPad2_NPE23() + { + StringUtils.rightPad( null, 0, "+" ); + } + + @Test + public void testRightPad2() + { + assertThat( StringUtils.rightPad( "dings", 0, "+" ) + , is( "dings" ) ); + + assertThat( StringUtils.rightPad( "dings", 3, "+" ) + , is( "dings" ) ); + + assertThat( StringUtils.rightPad( "dings", 10, "+" ) + , is( "dings+++++" ) ); + } + + + @Test( expected = NullPointerException.class ) + public void testSplit1_NPE() + { + StringUtils.split( null ); + } + + @Test + public void testSplit1() + { + assertThat( StringUtils.split( "dings" ) + , is( new String[]{ "dings" } ) ); + + assertThat( StringUtils.split( "dings bums" ) + , is( new String[]{ "dings", "bums" } ) ); + } + + @Test( expected = NullPointerException.class ) + public void testSplit2_NPE1() + { + StringUtils.split( null, null ); + } + + @Test( expected = NullPointerException.class ) + public void testSplit2_NPE2() + { + StringUtils.split( null, " " ); + } + + @Test + public void testSplit2() + { + assertThat( StringUtils.split( "dings", null ) + , is( new String[]{ "dings" } ) ); + + assertThat( StringUtils.split( "dings bums", null ) + , is( new String[]{ "dings", "bums" } ) ); + + assertThat( StringUtils.split( "dings", "+" ) + , is( new String[]{ "dings" } ) ); + + assertThat( StringUtils.split( "dings+bums", "+" ) + , is( new String[]{ "dings", "bums" } ) ); + } + + @Test( expected = NullPointerException.class ) + public void testSplit3_NPE1() + { + StringUtils.split( null, null, 1 ); + } + + @Test( expected = NullPointerException.class ) + public void testSplit3_NPE2() + { + StringUtils.split( null, " ", 1 ); + } + + @Test + public void testSplit3() + { + assertThat( StringUtils.split( "dings", null, 3 ) + , is( new String[]{ "dings" } ) ); + + assertThat( StringUtils.split( "dings bums", null, 3 ) + , is( new String[]{ "dings", "bums" } ) ); + + assertThat( StringUtils.split( "dings", "+", 3 ) + , is( new String[]{ "dings" } ) ); + + assertThat( StringUtils.split( "dings+bums", "+", 3 ) + , is( new String[]{ "dings", "bums" } ) ); + + assertThat( StringUtils.split( "dings+bums", "+", 1 ) + , is( new String[]{ "dings+bums" } ) ); + + assertThat( StringUtils.split( "dings+bums", "+", 0 ) + , is( new String[]{ "dings", "bums" } ) ); + + assertThat( StringUtils.split( "dings+bums", "+", -5 ) + , is( new String[]{ "dings", "bums" } ) ); + + } + + + @Test + public void testStrip1() + { + assertThat( StringUtils.strip( null ) + , nullValue() ); + + assertThat( StringUtils.strip( "dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.strip( " dings \t " ) + , is( "dings" ) ); + } + + @Test + public void testStrip2() + { + assertThat( StringUtils.strip( null, " " ) + , nullValue() ); + + assertThat( StringUtils.strip( null, null ) + , nullValue() ); + + assertThat( StringUtils.strip( "dings", " " ) + , is( "dings" ) ); + + assertThat( StringUtils.strip( " dings \t ", " " ) + , is( "dings \t" ) ); + } + + @Test + public void testStripAll1() + { + assertThat( StringUtils.stripAll( null ) + , nullValue() ); + + assertThat( StringUtils.stripAll( new String[]{} ) + , is( new String[]{} ) ); + + assertThat( StringUtils.stripAll( new String[]{ "dings" } ) + , is( new String[]{ "dings" } ) ); + + assertThat( StringUtils.stripAll( new String[]{ " dings ", " bums \t " } ) + , is( new String[]{ "dings", "bums" } ) ); + } + + @Test + public void testStripAll2() + { + assertThat( StringUtils.stripAll( null, " " ) + , nullValue() ); + + assertThat( StringUtils.stripAll( new String[]{}, " " ) + , is( new String[]{} ) ); + + assertThat( StringUtils.stripAll( new String[]{ "dings" }, " " ) + , is( new String[]{ "dings" } ) ); + + assertThat( StringUtils.stripAll( new String[]{ " dings ", " bums \t " }, " " ) + , is( new String[]{ "dings", "bums \t" } ) ); + } + + @Test + public void testStripEnd() + { + assertThat( StringUtils.stripEnd( null, null ) + , nullValue() ); + + assertThat( StringUtils.stripEnd( "dings", null ) + , is( "dings" ) ); + + assertThat( StringUtils.stripEnd( " dings \t ", null ) + , is( " dings" ) ); + + assertThat( StringUtils.stripEnd( null, " " ) + , nullValue() ); + + assertThat( StringUtils.stripEnd( "dings", " " ) + , is( "dings" ) ); + + assertThat( StringUtils.stripEnd( " dings \t ", " " ) + , is( " dings \t" ) ); + } + + @Test + public void testStripStart() + { + assertThat( StringUtils.stripStart( null, null ) + , nullValue() ); + + assertThat( StringUtils.stripStart( "dings", null ) + , is( "dings" ) ); + + assertThat( StringUtils.stripStart( " dings \t ", null ) + , is( "dings \t " ) ); + + assertThat( StringUtils.stripStart( null, " " ) + , nullValue() ); + + assertThat( StringUtils.stripStart( "dings", " " ) + , is( "dings" ) ); + + assertThat( StringUtils.stripStart( " \t dings \t ", " " ) + , is( "\t dings \t " ) ); + } + + @Test + public void testSubstring1() + { + assertThat( StringUtils.substring( null, 0 ) + , nullValue() ); + assertThat( StringUtils.substring( null, -3 ) + , nullValue() ); + + assertThat( StringUtils.substring( "dings", 2 ) + , is( "ngs" ) ); + + assertThat( StringUtils.substring( "dings", -2 ) + , is( "gs" ) ); + + assertThat( StringUtils.substring( "dings", 20 ) + , is( "" ) ); + } + + @Test + public void testSubstring2() + { + assertThat( StringUtils.substring( null, 0, 2 ) + , nullValue() ); + + assertThat( StringUtils.substring( null, -3, 0 ) + , nullValue() ); + + assertThat( StringUtils.substring( "dings", 2, 4 ) + , is( "ng" ) ); + + assertThat( StringUtils.substring( "dings", -2, 4 ) + , is( "g" ) ); + + assertThat( StringUtils.substring( "dings", 20, 23 ) + , is( "" ) ); + + assertThat( StringUtils.substring( "dings", 4, 2 ) + , is( "" ) ); + } + + @Test + public void testSwapCase() + { + assertThat( StringUtils.swapCase( null ) + , nullValue() ); + + assertThat( StringUtils.swapCase( "dings" ) + , is( "DINGS" ) ); + + assertThat( StringUtils.swapCase( "DinGs" ) + , is( "dINgS" ) ); + + } + + @Test + public void testTrim() + { + assertThat( StringUtils.trim( null ) + , nullValue() ); + + assertThat( StringUtils.trim( " " ) + , is( "" ) ); + + assertThat( StringUtils.trim( " c " ) + , is( "c" ) ); + + assertThat( StringUtils.trim( " dings \n " ) + , is( "dings" ) ); + } + + @Test + public void testUncapitalise() + { + assertThat( StringUtils.uncapitalise( null ) + , nullValue() ); + + assertThat( StringUtils.uncapitalise( " " ) + , is( " " ) ); + + assertThat( StringUtils.uncapitalise( "dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.uncapitalise( "Dings" ) + , is( "dings" ) ); + + assertThat( StringUtils.uncapitalise( "DINGS" ) + , is( "dINGS" ) ); + } + + @Test + public void testUncapitaliseAllWords() + { + assertThat( StringUtils.uncapitaliseAllWords( null ) + , nullValue() ); + + assertThat( StringUtils.uncapitaliseAllWords( " " ) + , is( " " ) ); + + assertThat( StringUtils.uncapitaliseAllWords( "dings bums" ) + , is( "dings bums" ) ); + + assertThat( StringUtils.uncapitaliseAllWords( "Dings Bums" ) + , is( "dings bums" ) ); + + assertThat( StringUtils.uncapitaliseAllWords( "DINGS Bums" ) + , is( "dINGS bums" ) ); + } + + @Test + public void testUnifyLineSeparators1() + { + String sls = System.getProperty( "line.separator" ); + + assertThat( StringUtils.unifyLineSeparators( null ) + , nullValue() ); + + assertThat( StringUtils.unifyLineSeparators( " " ) + , is( " " ) ); + + assertThat( StringUtils.unifyLineSeparators( "dings\nbums\r\ndongs" ) + , is( "dings" + sls + "bums" + sls + "dongs") ); + } + + @Test + public void testUnifyLineSeparators2() + { + assertThat( StringUtils.unifyLineSeparators( null, "\n" ) + , nullValue() ); + + assertThat( StringUtils.unifyLineSeparators( " ", "\n" ) + , is( " " ) ); + + assertThat( StringUtils.unifyLineSeparators( " ", null ) // takes the sytem line separator + , is( " " ) ); + + assertThat( StringUtils.unifyLineSeparators( "dings\nbums\r\ndongs", "\n" ) + , is( "dings\nbums\ndongs" ) ); + } + + @Test + public void testUppercase() + { + assertThat( StringUtils.upperCase( null ) + , nullValue() ); + + assertThat( StringUtils.upperCase( " " ) + , is( " " ) ); + + assertThat( StringUtils.upperCase( "" ) + , is( "" ) ); + + assertThat( StringUtils.upperCase( "dings" ) + , is( "DINGS" ) ); + + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/XmlStreamReaderTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/XmlStreamReaderTest.java new file mode 100644 index 000000000..2e3053829 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/XmlStreamReaderTest.java @@ -0,0 +1,208 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.SequenceInputStream; + + +import junit.framework.ComparisonFailure; +import junit.framework.TestCase; + +import org.apache.commons.io.IOUtils; +import org.apache.maven.shared.utils.xml.XmlStreamReader; + +/** + * + * @author Hervé Boutemy + */ +public class XmlStreamReaderTest + extends TestCase +{ + /** french */ + private static final String TEXT_LATIN1 = "eacute: \u00E9"; + /** greek */ + private static final String TEXT_LATIN7 = "alpha: \u03B1"; + /** euro support */ + private static final String TEXT_LATIN15 = "euro: \u20AC"; + /** japanese */ + private static final String TEXT_EUC_JP = "hiragana A: \u3042"; + /** Unicode: support everything */ + private static final String TEXT_UNICODE = + TEXT_LATIN1 + ", " + + TEXT_LATIN7 + ", " + + TEXT_LATIN15 + ", " + + TEXT_EUC_JP; + /** see http://unicode.org/faq/utf_bom.html#BOM */ + private static final byte[] BOM_UTF8 = { (byte)0xEF, (byte)0xBB, (byte)0xBF }; + private static final byte[] BOM_UTF16BE = { (byte)0xFE, (byte)0xFF }; + private static final byte[] BOM_UTF16LE = { (byte)0xFF, (byte)0xFE }; + + private static String createXmlContent( String text, String encoding ) + { + String xmlDecl = ""; + if ( encoding != null ) + { + xmlDecl = ""; + } + return xmlDecl + "\n" + text + ""; + } + + private static void checkXmlContent( String xml, String encoding ) throws IOException + { + checkXmlContent( xml, encoding, null ); + } + + private static void checkXmlContent( String xml, String encoding, byte[] bom ) throws IOException + { + byte[] xmlContent = xml.getBytes( encoding ); + InputStream in = new ByteArrayInputStream( xmlContent ); + + if ( bom != null ) + { + in = new SequenceInputStream( new ByteArrayInputStream( bom ), in ); + } + + XmlStreamReader reader = new XmlStreamReader( in ); + assertEquals( encoding, reader.getEncoding() ); + String result = IOUtils.toString( reader ); + assertEquals( xml, result ); + } + + private static void checkXmlStreamReader( String text, String encoding, String effectiveEncoding ) + throws IOException + { + checkXmlStreamReader( text, encoding, effectiveEncoding, null ); + } + + private static void checkXmlStreamReader( String text, String encoding ) throws IOException + { + checkXmlStreamReader( text, encoding, encoding, null ); + } + + private static void checkXmlStreamReader( String text, String encoding, byte[] bom ) throws IOException + { + checkXmlStreamReader( text, encoding, encoding, bom ); + } + + private static void checkXmlStreamReader( String text, String encoding, String effectiveEncoding, byte[] bom ) + throws IOException + { + String xml = createXmlContent( text, encoding ); + checkXmlContent( xml, effectiveEncoding, bom ); + } + + public void testNoXmlHeader() throws IOException + { + String xml = "text with no XML header"; + checkXmlContent( xml, "UTF-8" ); + checkXmlContent( xml, "UTF-8", BOM_UTF8 ); + } + + public void testDefaultEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, null, "UTF-8" ); + checkXmlStreamReader( TEXT_UNICODE, null, "UTF-8", BOM_UTF8 ); + } + + public void testUTF8Encoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-8" ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-8", BOM_UTF8 ); + } + + public void testUTF16Encoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16BE", null ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16LE", BOM_UTF16LE ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16BE", BOM_UTF16BE ); + } + + public void testUTF16BEEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16BE" ); + } + + public void testUTF16LEEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16LE" ); + } + + public void testLatin1Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN1, "ISO-8859-1" ); + } + + public void testLatin7Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN7, "ISO-8859-7" ); + } + + public void testLatin15Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN15, "ISO-8859-15" ); + } + + public void testEUC_JPEncoding() throws IOException + { + checkXmlStreamReader( TEXT_EUC_JP, "EUC-JP" ); + } + + public void testEBCDICEncoding() throws IOException + { + checkXmlStreamReader( "simple text in EBCDIC", "CP1047" ); + } + + public void testInappropriateEncoding() throws IOException + { + try + { + checkXmlStreamReader( TEXT_UNICODE, "ISO-8859-2" ); + fail( "Check should have failed, since some characters are not available in the specified encoding" ); + } + catch ( ComparisonFailure cf ) + { + // expected failure, since the encoding does not contain some characters + } + } + + public void testEncodingAttribute() throws IOException + { + String xml = ""; + checkXmlContent( xml, "US-ASCII" ); + + xml = ""; + checkXmlContent( xml, "US-ASCII" ); + + xml = ""; + checkXmlContent( xml, "UTF-8" ); + + xml = "\n"; + checkXmlContent( xml, "US-ASCII" ); + + xml = "\n"; + checkXmlContent( xml, "UTF-8" ); + + xml = ""; + checkXmlContent( xml, "UTF-8" ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java new file mode 100644 index 000000000..079d0d163 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java @@ -0,0 +1,171 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +import org.apache.maven.shared.utils.Os; + +import org.junit.Test; + +public class CommandLineUtilsTest +{ + + /** + * Tests that case-insensitive environment variables are normalized to upper case. + */ + @Test + public void testGetSystemEnvVarsCaseInsensitive() + { + Properties vars = CommandLineUtils.getSystemEnvVars( false ); + for ( Object o : vars.keySet() ) + { + String variable = (String) o; + assertEquals( variable.toUpperCase( Locale.ENGLISH ), variable ); + } + } + + @Test + public void testEnsureCaseSensitivity() + throws Exception + { + Map data = new HashMap(); + data.put( "abz", "cool" ); + assertTrue( CommandLineUtils.ensureCaseSensitivity( data, false ).containsKey( "ABZ" ) ); + assertTrue( CommandLineUtils.ensureCaseSensitivity( data, true ).containsKey( "abz" ) ); + } + + /** + * Tests that environment variables on Windows are normalized to upper case. Does nothing on Unix platforms. + */ + @Test + public void testGetSystemEnvVarsWindows() + throws Exception + { + if ( !Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + return; + } + Properties vars = CommandLineUtils.getSystemEnvVars(); + for ( Object o : vars.keySet() ) + { + String variable = (String) o; + assertEquals( variable.toUpperCase( Locale.ENGLISH ), variable ); + } + } + + /** + * Tests the splitting of a command line into distinct arguments. + */ + @Test + public void testTranslateCommandline() + throws Exception + { + assertCmdLineArgs( new String[] {}, null ); + assertCmdLineArgs( new String[] {}, "" ); + + assertCmdLineArgs( new String[] { "foo", "bar" }, "foo bar" ); + assertCmdLineArgs( new String[] { "foo", "bar" }, " foo bar " ); + + assertCmdLineArgs( new String[] { "foo", " double quotes ", "bar" }, "foo \" double quotes \" bar" ); + assertCmdLineArgs( new String[] { "foo", " single quotes ", "bar" }, "foo ' single quotes ' bar" ); + + assertCmdLineArgs( new String[] { "foo", " \" ", "bar" }, "foo ' \" ' bar" ); + assertCmdLineArgs( new String[] { "foo", " ' ", "bar" }, "foo \" ' \" bar" ); + } + + + @Test + public void givenASingleQuoteMarkInArgument_whenExecutingCode_thenNoExceptionIsThrown() throws Exception { + new Commandline("echo \"let's go\"").execute(); + } + + @Test + public void givenADoubleQuoteMarkInArgument_whenExecutingCode_thenCommandLineExceptionIsThrown() throws Exception { + try { + new Commandline("echo \"let\"s go\"").execute(); + } catch (CommandLineException e) { + assertTrue(true); + return; + } + fail("Exception was not thrown when given invalid (3 unescaped double quote) input"); + } + + + @Test + public void givenASingleQuoteMarkInArgument_whenExecutingCode_thenExitCode0Returned() throws Exception { + final Process p = new Commandline("echo \"let's go\"").execute(); + // Note, this sleep should be removed when java version reaches Java 8 + Thread.sleep(1000); + assertEquals(0, p.exitValue()); + } + + @Test + public void givenASingleQuoteMarkInArgument_whenTranslatingToCmdLineArgs_thenTheQuotationMarkIsNotEscaped() + throws Exception + { + final String command = "echo \"let's go\""; + final String[] expected = new String[] { "echo", "let's go" }; + assertCmdLineArgs( expected, command ); + } + + @Test + public void givenAnEscapedDoubleQuoteMarkInArgument_whenTranslatingToCmdLineArgs_thenTheQuotationMarkRemainsEscaped() + throws Exception + { + final String command = "echo \"let\\\"s go\""; + final String[] expected = new String[] { "echo", "let\\\"s go" }; + assertCmdLineArgs( expected, command ); + } + + @Test + public void givenAnEscapedSingleQuoteMarkInArgument_whenTranslatingToCmdLineArgs_thenTheQuotationMarkRemainsEscaped() + throws Exception + { + final String command = "echo \"let\\'s go\""; + final String[] expected = new String[] { "echo", "let\\'s go"}; + assertCmdLineArgs( expected, command ); + } + + @Test + public void givenAnEscapedDoubleQuoteMarkInArgument_whenTranslatingToCmdLineArgs_thenNoExceptionIsThrown() + throws Exception + { + new Commandline( "echo \"let\\\"s go\"" ).execute(); + } + + private void assertCmdLineArgs( final String[] expected, final String cmdLine ) + throws Exception + { + String[] actual = CommandLineUtils.translateCommandline( cmdLine ); + assertNotNull( actual ); + assertEquals( expected.length, actual.length ); + assertEquals( Arrays.asList( expected ), Arrays.asList( actual ) ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/cli/shell/BourneShellTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/cli/shell/BourneShellTest.java new file mode 100644 index 000000000..199f36e4b --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/cli/shell/BourneShellTest.java @@ -0,0 +1,206 @@ +package org.apache.maven.shared.utils.cli.shell; + +import java.util.List; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.StringUtils; +import org.apache.maven.shared.utils.cli.Commandline; + +import junit.framework.TestCase; + +public class BourneShellTest + extends TestCase +{ + + Shell newShell() + { + return new BourneShell(); + } + + public void testQuoteWorkingDirectoryAndExecutable() + { + Shell sh = newShell(); + + sh.setWorkingDirectory( "/usr/local/bin" ); + sh.setExecutable( "chmod" ); + + String executable = StringUtils.join( sh.getShellCommandLine().iterator(), " " ); + + assertEquals( "/bin/sh -c cd '/usr/local/bin' && 'chmod'", executable ); + } + + public void testQuoteWorkingDirectoryAndExecutable_WDPathWithSingleQuotes() + { + Shell sh = newShell(); + + sh.setWorkingDirectory( "/usr/local/'something else'" ); + sh.setExecutable( "chmod" ); + + String executable = StringUtils.join( sh.getShellCommandLine().iterator(), " " ); + + assertEquals( "/bin/sh -c cd '/usr/local/'\"'\"'something else'\"'\"'' && 'chmod'", executable ); + } + + public void testQuoteWorkingDirectoryAndExecutable_WDPathWithSingleQuotes_BackslashFileSep() + { + Shell sh = newShell(); + + sh.setWorkingDirectory( "\\usr\\local\\'something else'" ); + sh.setExecutable( "chmod" ); + + String executable = StringUtils.join( sh.getShellCommandLine().iterator(), " " ); + + assertEquals( "/bin/sh -c cd '\\usr\\local\\'\"'\"'something else'\"'\"'' && 'chmod'", executable ); + } + + public void testPreserveSingleQuotesOnArgument() + { + Shell sh = newShell(); + + sh.setWorkingDirectory( "/usr/bin" ); + sh.setExecutable( "chmod" ); + + List shellCommandLine = sh.getShellCommandLine("\"some arg with spaces\""); + + String cli = StringUtils.join( shellCommandLine.iterator(), " " ); + System.out.println( cli ); + assertTrue( cli.endsWith( "'\"some arg with spaces\"'" ) ); + } + + public void testAddSingleQuotesOnArgumentWithSpaces() + { + Shell sh = newShell(); + + sh.setWorkingDirectory( "/usr/bin" ); + sh.setExecutable( "chmod" ); + + List shellCommandLine = sh.getShellCommandLine("some arg with spaces"); + + String cli = StringUtils.join( shellCommandLine.iterator(), " " ); + System.out.println( cli ); + assertTrue( cli.endsWith("'some arg with spaces'")); + } + + public void testAddArgumentWithSingleQuote() + { + Shell sh = newShell(); + + sh.setWorkingDirectory( "/usr/bin" ); + sh.setExecutable( "chmod" ); + + List shellCommandLine = sh.getShellCommandLine("arg'withquote"); + + assertEquals("cd '/usr/bin' && 'chmod' 'arg'\"'\"'withquote'", shellCommandLine.get(shellCommandLine.size() - 1)); + } + + public void testArgumentsWithSemicolon() + { + + System.out.println( "---- semi colon tests ----" ); + + Shell sh = newShell(); + + sh.setWorkingDirectory( "/usr/bin" ); + sh.setExecutable( "chmod" ); + + List shellCommandLine = sh.getShellCommandLine(";some&argwithunix$chars"); + + String cli = StringUtils.join( shellCommandLine.iterator(), " " ); + System.out.println( cli ); + assertTrue( cli.endsWith( "';some&argwithunix$chars'" ) ); + + Commandline commandline = new Commandline( newShell() ); + commandline.setExecutable( "chmod" ); + commandline.getShell().setQuotedArgumentsEnabled( true ); + commandline.createArg().setValue( "--password" ); + commandline.createArg().setValue( ";password" ); + + List lines = commandline.getShell().getShellCommandLine( commandline.getArguments() ); + System.out.println( lines ); + + assertEquals( "/bin/sh", lines.get( 0 ) ); + assertEquals( "-c", lines.get( 1 ) ); + assertEquals( "'chmod' '--password' ';password'", lines.get( 2 ) ); + + commandline = new Commandline( newShell() ); + commandline.setExecutable( "chmod" ); + commandline.getShell().setQuotedArgumentsEnabled( true ); + commandline.createArg().setValue( "--password" ); + commandline.createArg().setValue( ";password" ); + lines = commandline.getShell().getShellCommandLine( commandline.getArguments() ); + System.out.println( lines ); + + assertEquals( "/bin/sh", lines.get( 0) ); + assertEquals( "-c", lines.get( 1 ) ); + assertEquals( "'chmod' '--password' ';password'", lines.get( 2 ) ); + + commandline = new Commandline( new CmdShell() ); + commandline.getShell().setQuotedArgumentsEnabled( true ); + commandline.createArg().setValue( "--password" ); + commandline.createArg().setValue( ";password" ); + lines = commandline.getShell().getShellCommandLine( commandline.getArguments() ); + System.out.println( lines ); + + assertEquals( "cmd.exe", lines.get( 0 ) ); + assertEquals( "/X", lines.get( 1 ) ); + assertEquals( "/C", lines.get( 2 ) ); + assertEquals( "\"--password ;password\"", lines.get( 3 ) ); + } + + public void testBourneShellQuotingCharacters() + throws Exception + { + // { ' ', '$', ';', '&', '|', '<', '>', '*', '?', '(', ')' }; + // test with values http://steve-parker.org/sh/bourne.shtml Appendix B - Meta-characters and Reserved Words + Commandline commandline = new Commandline( newShell() ); + commandline.setExecutable( "chmod" ); + commandline.getShell().setQuotedArgumentsEnabled( true ); + commandline.createArg().setValue( " " ); + commandline.createArg().setValue( "|" ); + commandline.createArg().setValue( "&&" ); + commandline.createArg().setValue( "||" ); + commandline.createArg().setValue( ";" ); + commandline.createArg().setValue( ";;" ); + commandline.createArg().setValue( "&" ); + commandline.createArg().setValue( "()" ); + commandline.createArg().setValue( "<" ); + commandline.createArg().setValue( "<<" ); + commandline.createArg().setValue( ">" ); + commandline.createArg().setValue( ">>" ); + commandline.createArg().setValue( "*" ); + commandline.createArg().setValue( "?" ); + commandline.createArg().setValue( "[" ); + commandline.createArg().setValue( "]" ); + commandline.createArg().setValue( "{" ); + commandline.createArg().setValue( "}" ); + commandline.createArg().setValue( "`" ); + commandline.createArg().setValue( "#" ); + + List lines = commandline.getShell().getShellCommandLine( commandline.getArguments() ); + System.out.println( lines ); + + assertEquals( "/bin/sh", lines.get( 0 ) ); + assertEquals( "-c", lines.get( 1 ) ); + assertEquals( "'chmod' ' ' '|' '&&' '||' ';' ';;' '&' '()' '<' '<<' '>' '>>' '*' '?' '[' ']' '{' '}' '`' '#'", + lines.get( 2 ) ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/exceptionutils/TestException.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/exceptionutils/TestException.java new file mode 100644 index 000000000..5ccd2e01a --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/exceptionutils/TestException.java @@ -0,0 +1,56 @@ +package org.apache.maven.shared.utils.exceptionutils; + +/* + * 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. + */ + +/** + * + * @author Mark Struberg + */ +public class TestException + extends Exception +{ + private Throwable cause; + private Throwable specialCause; + + public TestException() + { + super(); + } + + public void setSourceException( Throwable cause ) + { + this.cause = cause; + } + + public Throwable getSourceException() + { + return cause; + } + + public Throwable getSpecialCause() + { + return specialCause; + } + + public void setSpecialCause( Throwable specialCause ) + { + this.specialCause = specialCause; + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/exceptionutils/TestExceptionWithDetail.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/exceptionutils/TestExceptionWithDetail.java new file mode 100644 index 000000000..928b18cbb --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/exceptionutils/TestExceptionWithDetail.java @@ -0,0 +1,46 @@ +package org.apache.maven.shared.utils.exceptionutils; + +/* + * 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. + */ + +/** + * This test exception has a 'detail' field. + * + * @author Mark Struberg + */ +public class TestExceptionWithDetail + extends Exception +{ + private Throwable detail; + + public TestExceptionWithDetail() + { + super(); + } + + public Throwable getDetail() + { + return detail; + } + + public void setDetail( Throwable detail ) + { + this.detail = detail; + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java new file mode 100644 index 000000000..1c9a0e269 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/introspection/ReflectionValueExtractorTest.java @@ -0,0 +1,525 @@ +package org.apache.maven.shared.utils.introspection; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.plugin.testing.stubs.MavenProjectStub; + +import junit.framework.TestCase; + +/** + * @author Jason van Zyl + * + */ +public class ReflectionValueExtractorTest + extends TestCase +{ + private Project project; + + protected void setUp() + throws Exception + { + super.setUp(); + + Dependency dependency1 = new Dependency(); + dependency1.setArtifactId( "dep1" ); + Dependency dependency2 = new Dependency(); + dependency2.setArtifactId( "dep2" ); + + project = new Project(); + project.setModelVersion( "4.0.0" ); + project.setGroupId( "org.apache.maven" ); + project.setArtifactId( "maven-core" ); + project.setName( "Maven" ); + project.setVersion( "2.0-SNAPSHOT" ); + project.setScm( new Scm() ); + project.getScm().setConnection( "scm-connection" ); + project.addDependency( dependency1 ); + project.addDependency( dependency2 ); + project.setBuild( new Build() ); + + // Build up an artifactMap + project.addArtifact( new Artifact("g0","a0","v0","e0","c0") ); + project.addArtifact( new Artifact("g1","a1","v1","e1","c1") ); + project.addArtifact( new Artifact("g2","a2","v2","e2","c2") ); + } + + public void testValueExtraction() + throws Exception + { + // ---------------------------------------------------------------------- + // Top level values + // ---------------------------------------------------------------------- + + assertEquals( "4.0.0", ReflectionValueExtractor.evaluate( "project.modelVersion", project ) ); + + assertEquals( "org.apache.maven", ReflectionValueExtractor.evaluate( "project.groupId", project ) ); + + assertEquals( "maven-core", ReflectionValueExtractor.evaluate( "project.artifactId", project ) ); + + assertEquals( "Maven", ReflectionValueExtractor.evaluate( "project.name", project ) ); + + assertEquals( "2.0-SNAPSHOT", ReflectionValueExtractor.evaluate( "project.version", project ) ); + + // ---------------------------------------------------------------------- + // SCM + // ---------------------------------------------------------------------- + + assertEquals( "scm-connection", ReflectionValueExtractor.evaluate( "project.scm.connection", project ) ); + + // ---------------------------------------------------------------------- + // Dependencies + // ---------------------------------------------------------------------- + + List dependencies = (List) ReflectionValueExtractor.evaluate( "project.dependencies", project ); + + assertNotNull( dependencies ); + + assertEquals( 2, dependencies.size() ); + + // ---------------------------------------------------------------------- + // Dependencies - using index notation + // ---------------------------------------------------------------------- + + // List + Dependency dependency = (Dependency) ReflectionValueExtractor.evaluate( "project.dependencies[0]", project ); + + assertNotNull( dependency ); + + assertTrue( "dep1".equals( dependency.getArtifactId() ) ); + + String artifactId = (String) ReflectionValueExtractor.evaluate( "project.dependencies[1].artifactId", project ); + + assertTrue( "dep2".equals( artifactId ) ); + + // Array + + dependency = (Dependency) ReflectionValueExtractor.evaluate( "project.dependenciesAsArray[0]", project ); + + assertNotNull( dependency ); + + assertTrue( "dep1".equals( dependency.getArtifactId() ) ); + + artifactId = (String) ReflectionValueExtractor.evaluate( "project.dependenciesAsArray[1].artifactId", project ); + + assertTrue( "dep2".equals( artifactId ) ); + + // Map + + dependency = (Dependency) ReflectionValueExtractor.evaluate( "project.dependenciesAsMap(dep1)", project ); + + assertNotNull( dependency ); + + assertTrue( "dep1".equals( dependency.getArtifactId() ) ); + + artifactId = (String) ReflectionValueExtractor.evaluate( "project.dependenciesAsMap(dep2).artifactId", project ); + + assertTrue( "dep2".equals( artifactId ) ); + + // ---------------------------------------------------------------------- + // Build + // ---------------------------------------------------------------------- + + Build build = (Build) ReflectionValueExtractor.evaluate( "project.build", project ); + + assertNotNull( build ); + } + + public void testValueExtractorWithAInvalidExpression() + throws Exception + { + assertNull( ReflectionValueExtractor.evaluate( "project.foo", project ) ); + assertNull( ReflectionValueExtractor.evaluate( "project.dependencies[10]", project ) ); + assertNull( ReflectionValueExtractor.evaluate( "project.dependencies[0].foo", project ) ); + } + + public void testMappedDottedKey() + throws Exception + { + Map map = new HashMap(); + map.put( "a.b", "a.b-value" ); + + assertEquals( "a.b-value", ReflectionValueExtractor.evaluate("h.value(a.b)", new ValueHolder(map)) ); + } + + public void testIndexedMapped() + throws Exception + { + Map map = new HashMap(); + map.put( "a", "a-value" ); + List list = new ArrayList(); + list.add( map ); + + assertEquals( "a-value", ReflectionValueExtractor.evaluate("h.value[0](a)", new ValueHolder(list)) ); + } + + public void testMappedIndexed() + throws Exception + { + List list = new ArrayList(); + list.add( "a-value" ); + Map map = new HashMap(); + map.put( "a", list ); + assertEquals( "a-value", ReflectionValueExtractor.evaluate("h.value(a)[0]", new ValueHolder(map)) ); + } + + public void testMappedMissingDot() + throws Exception + { + Map map = new HashMap(); + map.put( "a", new ValueHolder( "a-value" ) ); + assertNull( ReflectionValueExtractor.evaluate("h.value(a)value", new ValueHolder(map)) ); + } + + public void testIndexedMissingDot() + throws Exception + { + List list = new ArrayList(); + list.add( new ValueHolder( "a-value" ) ); + assertNull( ReflectionValueExtractor.evaluate("h.value[0]value", new ValueHolder(list)) ); + } + + public void testDotDot() + throws Exception + { + assertNull( ReflectionValueExtractor.evaluate("h..value", new ValueHolder("value")) ); + } + + public void testBadIndexedSyntax() + throws Exception + { + List list = new ArrayList(); + list.add( "a-value" ); + Object value = new ValueHolder( list ); + + assertNull( ReflectionValueExtractor.evaluate("h.value[", value) ); + assertNull( ReflectionValueExtractor.evaluate("h.value[]", value) ); + assertNull( ReflectionValueExtractor.evaluate("h.value[a]", value) ); + assertNull( ReflectionValueExtractor.evaluate("h.value[0", value) ); + assertNull( ReflectionValueExtractor.evaluate("h.value[0)", value) ); + assertNull( ReflectionValueExtractor.evaluate("h.value[-1]", value) ); + } + + public void testBadMappedSyntax() + throws Exception + { + Map map = new HashMap(); + map.put( "a", "a-value" ); + Object value = new ValueHolder( map ); + + assertNull( ReflectionValueExtractor.evaluate("h.value(", value) ); + assertNull( ReflectionValueExtractor.evaluate("h.value()", value) ); + assertNull( ReflectionValueExtractor.evaluate("h.value(a", value) ); + assertNull( ReflectionValueExtractor.evaluate("h.value(a]", value) ); + } + + public void testIllegalIndexedType() + throws Exception + { + try + { + ReflectionValueExtractor.evaluate("h.value[1]", new ValueHolder("string")); + } + catch ( Exception e ) + { + // TODO assert exception message + } + } + + public void testIllegalMappedType() + throws Exception + { + try + { + ReflectionValueExtractor.evaluate("h.value(key)", new ValueHolder("string")); + } + catch ( Exception e ) + { + // TODO assert exception message + } + } + + public void testTrimRootToken() + throws Exception + { + assertNull( ReflectionValueExtractor.evaluate("project", project, true) ); + } + + public void testArtifactMap() + throws Exception + { + assertEquals( "g0", ((Artifact) ReflectionValueExtractor.evaluate("project.artifactMap(g0:a0:c0)", project)).getGroupId() ); + assertEquals( "a1", ((Artifact) ReflectionValueExtractor.evaluate("project.artifactMap(g1:a1:c1)", project)).getArtifactId() ); + assertEquals( "c2", ((Artifact) ReflectionValueExtractor.evaluate("project.artifactMap(g2:a2:c2)", project)).getClassifier() ); + } + + public static class Artifact + { + private String groupId; + private String artifactId; + private String version; + private String extension; + private String classifier; + + public Artifact( String groupId, String artifactId, String version, String extension, String classifier ) + { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.extension = extension; + this.classifier = classifier; + } + + public String getGroupId() + { + return groupId; + } + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + public String getArtifactId() + { + return artifactId; + } + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + public String getVersion() + { + return version; + } + public void setVersion( String version ) + { + this.version = version; + } + public String getExtension() + { + return extension; + } + public void setExtension( String extension ) + { + this.extension = extension; + } + public String getClassifier() + { + return classifier; + } + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + } + + public static class Project + { + private String modelVersion; + + private String groupId; + + private Scm scm; + + private final List dependencies = new ArrayList(); + + private Build build; + + private String artifactId; + + private String name; + + private String version; + + private Map artifactMap = new HashMap(); + + public void setModelVersion( String modelVersion ) + { + this.modelVersion = modelVersion; + } + + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + + public void setScm( Scm scm ) + { + this.scm = scm; + } + + public void addDependency( Dependency dependency ) + { + this.dependencies.add( dependency ); + } + + public void setBuild( Build build ) + { + this.build = build; + } + + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + + public void setName( String name ) + { + this.name = name; + } + + public void setVersion( String version ) + { + this.version = version; + } + + public Scm getScm() + { + return scm; + } + + public String getModelVersion() + { + return modelVersion; + } + + public String getGroupId() + { + return groupId; + } + + public List getDependencies() + { + return dependencies; + } + + public Build getBuild() + { + return build; + } + + public String getArtifactId() + { + return artifactId; + } + + public String getName() + { + return name; + } + + public String getVersion() + { + return version; + } + + public Dependency[] getDependenciesAsArray() + { + List list = getDependencies(); + return list.toArray( new Dependency[list.size()] ); + } + + public Map getDependenciesAsMap() + { + Map ret = new HashMap(); + for ( Object o : getDependencies() ) + { + Dependency dep = (Dependency) o; + ret.put( dep.getArtifactId(), dep ); + } + return ret; + } + + + // ${project.artifactMap(g:a:v)} + public void addArtifact(Artifact a) + { + artifactMap.put( a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getClassifier(), a ); + } + + public Map getArtifactMap() + { + return artifactMap; + } + } + + + public static class Build + { + + } + + public static class Dependency + { + private String artifactId; + + public String getArtifactId() + { + return artifactId; + } + + public void setArtifactId( String id ) + { + artifactId = id; + } + } + + public static class Scm + { + private String connection; + + public void setConnection( String connection ) + { + this.connection = connection; + } + + public String getConnection() + { + return connection; + } + } + + public static class ValueHolder + { + private final Object value; + + public ValueHolder( Object value ) + { + this.value = value; + } + + public Object getValue() + { + return value; + } + } + + public void testRootPropertyRegression() + throws Exception + { + MavenProjectStub project = new MavenProjectStub( ); + project.setDescription( "c:\\\\org\\apache\\test" ); + Object evalued = ReflectionValueExtractor.evaluate( "description", project ); + assertNotNull( evalued ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/DirectoryScannerTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/DirectoryScannerTest.java new file mode 100644 index 000000000..011100693 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/DirectoryScannerTest.java @@ -0,0 +1,452 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.testhelpers.FileTestHelper; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; + +@SuppressWarnings( "deprecation" ) +public class DirectoryScannerTest +{ + private static final String[] NONE = new String[0]; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + private void createTestData() + throws IOException + { + File rootDir = tempFolder.getRoot(); + File folder1 = new File( rootDir, "folder1" ); + if ( !folder1.mkdirs() ) + { + Assert.fail(); + } + + FileTestHelper.generateTestFile( new File( rootDir, "file1.txt" ), 11 ); + FileTestHelper.generateTestFile( new File( rootDir, "file2.txt" ), 12 ); + FileTestHelper.generateTestFile( new File( rootDir, "file3.dat" ), 13 ); + + FileTestHelper.generateTestFile( new File( folder1, "file4.txt" ), 14 ); + FileTestHelper.generateTestFile( new File( folder1, "file5.dat" ), 15 ); + + File folder2 = new File( folder1, "ignorefolder" ); + if ( !folder2.mkdirs() ) + { + Assert.fail(); + } + FileTestHelper.generateTestFile( new File( folder2, "file7.txt" ), 17 ); + } + + @Test + public void testSimpleScan() + throws Exception + { + createTestData(); + + fitScanTest( true, true, true, + /* includes */ null, + /* excludes */ null, + /* expInclFiles */ + new String[]{ "file1.txt", "file2.txt", "file3.dat", "folder1/file4.txt", "folder1/file5.dat" }, + /* expInclDirs */ new String[]{ "", "folder1" }, + /* expNotInclFiles */ NONE, + /* expNotInclDirs */ NONE, + /* expNotExclFiles */ NONE, + /* expNotExclDirs */ NONE ); + + // same without followSymlinks + fitScanTest( true, false, true, + /* includes */ null, + /* excludes */ null, + /* expInclFiles */ + new String[]{ "file1.txt", "file2.txt", "file3.dat", "folder1/file4.txt", "folder1/file5.dat" }, + /* expInclDirs */ new String[]{ "", "folder1" }, + /* expNotInclFiles */ NONE, + /* expNotInclDirs */ NONE, + /* expNotExclFiles */ NONE, + /* expNotExclDirs */ NONE ); + } + + @Test + public void testSimpleIncludes() + throws Exception + { + createTestData(); + + fitScanTest( true, true, true, + /* includes */ new String[]{ "**/*.dat", "*.somethingelse" }, + /* excludes */ null, + /* expInclFiles */ new String[]{ "file3.dat", "folder1/file5.dat" }, + /* expInclDirs */ NONE, + /* expNotInclFiles */ new String[]{ "file1.txt", "file2.txt", "folder1/file4.txt" }, + /* expNotInclDirs */ new String[]{ "", "folder1" }, + /* expExclFiles */ NONE, + /* expExclDirs */ NONE ); + + // same without followSymlinks + fitScanTest( true, false, true, + /* includes */ new String[]{ "**/*.dat", "*.somethingelse" }, + /* excludes */ null, + /* expInclFiles */ new String[]{ "file3.dat", "folder1/file5.dat" }, + /* expInclDirs */ NONE, + /* expNotInclFiles */ new String[]{ "file1.txt", "file2.txt", "folder1/file4.txt" }, + /* expNotInclDirs */ new String[]{ "", "folder1" }, + /* expExclFiles */ NONE, + /* expExclDirs */ NONE ); + } + + @Test + public void checkSymlinkBehaviour() + { + DirectoryScanner ds = new DirectoryScanner(); + ds.setBasedir( new File( "src/test/resources/symlinks/src" ) ); + ds.setFollowSymlinks( false ); + ds.scan(); + + String[] includedDirectories = ds.getIncludedDirectories(); + // FIXME 3 (Windows) and 5 (Linux) are both wrong. The correct answer is 4. + // This method is broken in different ways on different operating systems. + assertTrue( includedDirectories.length == 3 || includedDirectories.length == 5); + + String[] files = ds.getIncludedFiles(); + assertAlwaysIncluded( Arrays.asList( files ) ); + + // FIXME getIncludedFiles is broken on Windows; correct answer is 9 + assertTrue("files.length is " + files.length, files.length == 9 || files.length == 11 ); + } + + @Test + public void followSymlinksFalse() + throws IOException + { + assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) ); + + File testDir = SymlinkTestSetup.createStandardSymlinkTestDir( new File( "target/test/symlinkTestCase" ) ); + + DirectoryScanner ds = new DirectoryScanner(); + ds.setBasedir( testDir ); + ds.setFollowSymlinks( false ); + ds.scan(); + List included = Arrays.asList( ds.getIncludedFiles() ); + assertAlwaysIncluded( included ); + assertEquals( 9, included.size() ); + List includedDirs = Arrays.asList( ds.getIncludedDirectories() ); + assertTrue( includedDirs.contains( "" ) ); + assertTrue( includedDirs.contains( "aRegularDir" ) ); + assertTrue( includedDirs.contains( "symDir" ) ); + assertTrue( includedDirs.contains( "symLinkToDirOnTheOutside" ) ); + assertTrue( includedDirs.contains( "targetDir" ) ); + assertEquals( 5, includedDirs.size() ); + } + + private void assertAlwaysIncluded( List included ) + { + assertTrue( included.contains( "aRegularDir" + File.separator + "aRegularFile.txt" ) ); + assertTrue( included.contains( "targetDir" + File.separator + "targetFile.txt" ) ); + assertTrue( included.contains( "fileR.txt" ) ); + assertTrue( included.contains( "fileW.txt" ) ); + assertTrue( included.contains( "fileX.txt" ) ); + assertTrue( included.contains( "symR" ) ); + assertTrue( included.contains( "symW" ) ); + assertTrue( included.contains( "symX" ) ); + assertTrue( included.contains( "symLinkToFileOnTheOutside" ) ); + } + + @Test + public void followSymlinks() + throws IOException + { + assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) ); + + DirectoryScanner ds = new DirectoryScanner(); + File testDir = SymlinkTestSetup.createStandardSymlinkTestDir( new File( "target/test/symlinkTestCase" ) ); + + ds.setBasedir( testDir ); + ds.setFollowSymlinks( true ); + ds.scan(); + List included = Arrays.asList( ds.getIncludedFiles() ); + assertAlwaysIncluded( included ); + assertTrue( included.contains( "symDir/targetFile.txt" ) ); + assertTrue( included.contains( "symLinkToDirOnTheOutside/FileInDirOnTheOutside.txt" ) ); + assertEquals( 11, included.size() ); + + List includedDirs = Arrays.asList( ds.getIncludedDirectories() ); + assertTrue( includedDirs.contains( "" ) ); // w00t ! + assertTrue( includedDirs.contains( "aRegularDir" ) ); + assertTrue( includedDirs.contains( "symDir" ) ); + assertTrue( includedDirs.contains( "symLinkToDirOnTheOutside" ) ); + assertTrue( includedDirs.contains( "targetDir" ) ); + assertEquals( 5, includedDirs.size() ); + } + + /* + Creates a standard directory layout with symlinks and files. + */ + @Test + public void testSimpleExcludes() + throws Exception + { + createTestData(); + + fitScanTest( true, true, true, + /* includes */ null, + /* excludes */ new String[]{ "**/*.dat", "*.somethingelse" }, + /* expInclFiles */ new String[]{ "file1.txt", "file2.txt", "folder1/file4.txt" }, + /* expInclDirs */ new String[]{ "", "folder1" }, + /* expNotInclFiles */ NONE, + /* expNotInclDirs */ NONE, + /* expExclFiles */ new String[]{ "file3.dat", "folder1/file5.dat" }, + /* expExclDirs */ NONE ); + + // same without followSymlinks + fitScanTest( true, false, true, + /* includes */ null, + /* excludes */ new String[]{ "**/*.dat", "*.somethingelse" }, + /* expInclFiles */ new String[]{ "file1.txt", "file2.txt", "folder1/file4.txt" }, + /* expInclDirs */ new String[]{ "", "folder1" }, + /* expNotInclFiles */ NONE, + /* expNotInclDirs */ NONE, + /* expExclFiles */ new String[]{ "file3.dat", "folder1/file5.dat" }, + /* expExclDirs */ NONE ); + } + + public void testIsSymbolicLink() + throws IOException + { + File file = new File( "." ); + DirectoryScanner ds = new DirectoryScanner(); + ds.isSymbolicLink( file, "abc" ); + } + + /** + * Performs a scan and test for the given parameters if not null. + */ + private void fitScanTest( boolean caseSensitive, boolean followSymLinks, boolean addDefaultExcludes, + String[] includes, String[] excludes, String[] expectedIncludedFiles, + String[] expectedIncludedDirectories, String[] expectedNotIncludedFiles, + String[] expectedNotIncludedDirectories, String[] expectedExcludedFiles, + String[] expectedExcludedDirectories ) + { + DirectoryScanner ds = new DirectoryScanner(); + ds.setBasedir( tempFolder.getRoot() ); + + ds.setCaseSensitive( caseSensitive ); + ds.setFollowSymlinks( followSymLinks ); + + if ( addDefaultExcludes ) + { + ds.addDefaultExcludes(); + } + if ( includes != null ) + { + ds.setIncludes( includes ); + } + if ( excludes != null ) + { + ds.setExcludes( excludes ); + } + + TestScanConductor scanConductor = new TestScanConductor(); + + ds.setScanConductor( scanConductor ); + + ds.scan(); + + checkFiles( "expectedIncludedFiles", expectedIncludedFiles, ds.getIncludedFiles() ); + checkFiles( "expectedIncludedDirectories", expectedIncludedDirectories, ds.getIncludedDirectories() ); + checkFiles( "expectedNotIncludedFiles", expectedNotIncludedFiles, ds.getNotIncludedFiles() ); + checkFiles( "expectedNotIncludedDirectories", expectedNotIncludedDirectories, ds.getNotIncludedDirectories() ); + checkFiles( "expectedExcludedFiles", expectedExcludedFiles, ds.getExcludedFiles() ); + checkFiles( "expectedExcludedDirectories", expectedExcludedDirectories, ds.getExcludedDirectories() ); + + checkFiles( "visitedFiles", expectedIncludedFiles, + scanConductor.visitedFiles.toArray( new String[scanConductor.visitedFiles.size()] ) ); + } + + /** + * Check if the resolved files match the rules of the expected files. + * + * @param expectedFiles + * @param resolvedFiles + */ + private void checkFiles( String category, String[] expectedFiles, String[] resolvedFiles ) + { + if ( expectedFiles != null ) + { + String msg = category + " expected: " + Arrays.toString( expectedFiles ) + " but got: " + Arrays.toString( + resolvedFiles ); + Assert.assertNotNull( msg, resolvedFiles ); + assertEquals( msg, expectedFiles.length, resolvedFiles.length ); + + Arrays.sort( expectedFiles ); + Arrays.sort( resolvedFiles ); + + for ( int i = 0; i < resolvedFiles.length; i++ ) + { + assertEquals( msg, expectedFiles[i], resolvedFiles[i].replace( "\\", "/" ) ); + } + } + } + + private static class TestScanConductor + implements ScanConductor + { + final List visitedFiles = new ArrayList(); + + public ScanConductor.ScanAction visitDirectory( String name, File directory ) + { + assertTrue( directory.isDirectory() ); + + if ( directory.getName().equals( "ignorefolder" ) ) + { + return ScanAction.NO_RECURSE; + } + + return ScanAction.CONTINUE; + } + + public ScanConductor.ScanAction visitFile( String name, File file ) + { + assertTrue( file.isFile() ); + visitedFiles.add( name ); + return ScanAction.CONTINUE; + } + } + + private void removeAndAddSomeFiles() + throws IOException + { + File rootDir = tempFolder.getRoot(); + File file2 = new File( rootDir, "file2.txt" ); + file2.delete(); + + FileTestHelper.generateTestFile( new File( rootDir, "folder1/file9.txt" ), 15 ); + + File folder2 = new File( rootDir, "folder1/ignorefolder" ); + FileUtils.deleteDirectory( folder2 ); + } + + @Test + public void testScanDiff() + throws Exception + { + createTestData(); + + DirectoryScanner dss = new DirectoryScanner(); + dss.setBasedir( tempFolder.getRoot() ); + Assert.assertNotNull( dss ); + + // we take the initial snapshot + dss.scan(); + String[] oldFiles = dss.getIncludedFiles(); + + // now we change 3 files. add one and remove + removeAndAddSomeFiles(); + + dss.scan(); + + DirectoryScanResult dsr = dss.diffIncludedFiles( oldFiles ); + + String[] addedFiles = dsr.getFilesAdded(); + String[] removedFiles = dsr.getFilesRemoved(); + Assert.assertNotNull( addedFiles ); + Assert.assertNotNull( removedFiles ); + assertEquals( 1, addedFiles.length ); + assertEquals( 2, removedFiles.length ); + } + + @Ignore( "Enable this test to run performance checks" ) + @Test + public void performanceTest() + throws Exception + { + + File rootFolder = tempFolder.getRoot(); + + // do some warmup + for ( int i = 1; i < 200; i++ ) + { + createTestData(); + removeAndAddSomeFiles(); + FileUtils.deleteDirectory( rootFolder ); + } + + int cycles = 2000; + + // and now we take the time _without_ + long startTime = System.nanoTime(); + for ( int i = 1; i < cycles; i++ ) + { + createTestData(); + removeAndAddSomeFiles(); + FileUtils.deleteDirectory( rootFolder ); + rootFolder.mkdir(); + } + long endTime = System.nanoTime(); + + long durationEmptyRun = endTime - startTime; + System.out.println( "durationEmptyRun [ns]: " + durationEmptyRun ); + + startTime = System.nanoTime(); + for ( int i = 1; i < cycles; i++ ) + { + createTestData(); + DirectoryScanner directoryScanner = new DirectoryScanner(); + directoryScanner.setBasedir( rootFolder ); + directoryScanner.scan(); + String[] oldFiles = directoryScanner.getIncludedFiles(); + + removeAndAddSomeFiles(); + + directoryScanner.scan(); + + DirectoryScanResult directoryScanResult = directoryScanner.diffIncludedFiles( oldFiles ); + Assert.assertNotNull( directoryScanResult ); + + FileUtils.deleteDirectory( rootFolder ); + rootFolder.mkdir(); + } + endTime = System.nanoTime(); + + long durationWithSnapshotScanner = endTime - startTime; + System.out.println( "durationWithSnapshotScanner [ns]: " + durationWithSnapshotScanner ); + + long dirScannerOverhead = durationWithSnapshotScanner - durationEmptyRun; + + System.out.println( "Overhead for n cycles [ns]: " + dirScannerOverhead ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/FileUtilsTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/FileUtilsTest.java new file mode 100644 index 000000000..9fa7c85e0 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/FileUtilsTest.java @@ -0,0 +1,1785 @@ +package org.apache.maven.shared.utils.io; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeThat; + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.testhelpers.FileTestHelper; +import org.codehaus.plexus.util.InterpolationFilterReader; +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestName; + +import javax.annotation.Nonnull; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/* + * 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. + */ + +/** + * This is used to test FileUtils for correctness. + * + * @author Peter Donald + * @author Matthew Hawthorne + * @author Stephen Colebourne + * @author Jim Harrington + * @version $Id: FileUtilsTestCase.java 1081025 2011-03-13 00:45:10Z niallp $ + * @see FileUtils + */ +@SuppressWarnings( "deprecation" ) +public class FileUtilsTest +{ + + // Test data + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Rule + public TestName name = new TestName(); + + /** + * Size of test directory. + */ + private static final int TEST_DIRECTORY_SIZE = 0; + + private File testFile1; + + private File testFile2; + + private long testFile1Size; + + private long testFile2Size; + + /** + * @see junit.framework.TestCase#setUp() + */ + @Before + public void setUp() + throws Exception + { + testFile1 = tempFolder.newFile( "file1-test.txt" ); + testFile2 = tempFolder.newFile( "file1a-test.txt" ); + + testFile1Size = (int) testFile1.length(); + testFile2Size = (int) testFile2.length(); + + tempFolder.getRoot().mkdirs(); + createFile( testFile1, testFile1Size ); + createFile( testFile2, testFile2Size ); + FileUtils.deleteDirectory( tempFolder.getRoot() ); + tempFolder.getRoot().mkdirs(); + createFile( testFile1, testFile1Size ); + createFile( testFile2, testFile2Size ); + } + + void createFile( File file, long size ) + throws IOException + { + if ( !file.getParentFile().exists() ) + { + throw new IOException( "Cannot create file " + file + " as the parent directory does not exist" ); + } + + try (OutputStream out = new BufferedOutputStream( new FileOutputStream( file ) ) ) + { + FileTestHelper.generateTestData( out, size ); + } + } + + + /** + * Assert that the content of a file is equal to that in a byte[]. + */ + void assertEqualContent( byte[] b0, File file ) + throws IOException + { + int count = 0, numRead = 0; + byte[] b1 = new byte[b0.length]; + try ( InputStream is = new FileInputStream( file ) ) + { + while ( count < b0.length && numRead >= 0 ) + { + numRead = is.read( b1, count, b0.length ); + count += numRead; + } + assertThat( "Different number of bytes: ", count, is( b0.length ) ); + for ( int i = 0; i < count; i++ ) + { + assertThat( "byte " + i + " differs", b1[i], is( b0[i] ) ); + } + } + } + + void deleteFile( File file ) + { + if ( file.exists() ) + { + assertThat( "Couldn't delete file: " + file, file.delete(), is( true ) ); + } + } + + + //----------------------------------------------------------------------- + @Test + public void toFile1() + throws Exception + { + URL url = new URL( "file", null, "a/b/c/file.txt" ); + File file = FileUtils.toFile( url ); + assertThat( file.toString(), containsString( "file.txt" ) ); + } + + @Test + public void toFile2() + throws Exception + { + URL url = new URL( "file", null, "a/b/c/file%20n%61me%2520.tx%74" ); + File file = FileUtils.toFile( url ); + assertThat( file.toString(), containsString( "file name%20.txt" ) ); + } + + @Test + public void toFile3() + throws Exception + { + assertThat( FileUtils.toFile( null ), CoreMatchers.nullValue() ); + assertThat( FileUtils.toFile( new URL( "http://jakarta.apache.org" ) ), CoreMatchers.nullValue() ); + } + + @Test( expected = NumberFormatException.class ) + public void toFile4() + throws Exception + { + URL url = new URL( "file", null, "a/b/c/file%%20%me.txt%" ); + File file = FileUtils.toFile( url ); + assertThat( file.toString(), containsString( "file% %me.txt%" ) ); + } + + /** + * IO-252 + */ + @Test + public void toFile5() + throws Exception + { + URL url = new URL( "file", null, "both%20are%20100%20%25%20true" ); + File file = FileUtils.toFile( url ); + assertThat( file.toString(), is( "both are 100 % true" ) ); + } + + @Test + public void toFileUtf8() + throws Exception + { + URL url = new URL( "file", null, "/home/%C3%A4%C3%B6%C3%BC%C3%9F" ); + File file = FileUtils.toFile( url ); + assertThat( file.toString(), not( containsString( "\u00E4\u00F6\u00FC\u00DF" ) ) ); + } + + // toURLs + + @Test + public void toURLs1() + throws Exception + { + File[] files = new File[]{ new File( tempFolder.getRoot(), "file1.txt" ), new File( tempFolder.getRoot(), "file2.txt" ), + new File( tempFolder.getRoot(), "test file.txt" ), }; + URL[] urls = FileUtils.toURLs( files ); + + assertThat( urls.length, is( files.length ) ); + assertThat( urls[0].toExternalForm().startsWith( "file:" ), is( true ) ); + assertThat( urls[0].toExternalForm().contains( "file1.txt" ), is( true ) ); + assertThat( urls[1].toExternalForm().startsWith( "file:" ), is( true ) ); + assertThat( urls[1].toExternalForm(), containsString( "file2.txt" ) ); + + // Test escaped char + assertThat( urls[2].toExternalForm().startsWith( "file:" ), is( true ) ); + assertThat( urls[2].toExternalForm(), containsString( "test%20file.txt" ) ); + } + + // contentEquals + + @Test + public void contentEquals() + throws Exception + { + // Non-existent files + File file = new File( tempFolder.getRoot(), name.getMethodName() ); + File file2 = new File( tempFolder.getRoot(), name.getMethodName() + "2" ); + // both don't exist + assertThat( FileUtils.contentEquals( file, file ), is( true ) ); + assertThat( FileUtils.contentEquals( file, file2 ), is( true ) ); + assertThat( FileUtils.contentEquals( file2, file2 ), is( true ) ); + assertThat( FileUtils.contentEquals( file2, file ), is( true ) ); + + // Directories + FileUtils.contentEquals( tempFolder.getRoot(), tempFolder.getRoot() ); + + // Different files + File objFile1 = new File( tempFolder.getRoot(), name.getMethodName() + ".object" ); + objFile1.deleteOnExit(); + FileUtils.copyURLToFile( getClass().getResource( "/java/lang/Object.class" ), objFile1 ); + + File objFile1b = new File( tempFolder.getRoot(), name.getMethodName() + ".object2" ); + objFile1.deleteOnExit(); + FileUtils.copyURLToFile( getClass().getResource( "/java/lang/Object.class" ), objFile1b ); + + File objFile2 = new File( tempFolder.getRoot(), name.getMethodName() + ".collection" ); + objFile2.deleteOnExit(); + FileUtils.copyURLToFile( getClass().getResource( "/java/util/Collection.class" ), objFile2 ); + + assertThat( FileUtils.contentEquals( objFile1, objFile2 ), is( false ) ); + assertThat( FileUtils.contentEquals( objFile1b, objFile2 ), is( false ) ); + assertThat( FileUtils.contentEquals( objFile1, objFile1b ), is( true ) ); + + assertThat( FileUtils.contentEquals( objFile1, objFile1 ), is( true ) ); + assertThat( FileUtils.contentEquals( objFile1b, objFile1b ), is( true ) ); + assertThat( FileUtils.contentEquals( objFile2, objFile2 ), is( true ) ); + + // Equal files + file.createNewFile(); + file2.createNewFile(); + assertThat( FileUtils.contentEquals( file, file ), is( true ) ); + assertThat( FileUtils.contentEquals( file, file2 ), is( true ) ); + } + + // copyURLToFile + + @Test + public void copyURLToFile() + throws Exception + { + // Creates file + File file = new File( tempFolder.getRoot(), name.getMethodName() ); + file.deleteOnExit(); + + // Loads resource + String resourceName = "/java/lang/Object.class"; + FileUtils.copyURLToFile( getClass().getResource( resourceName ), file ); + + // Tests that resource was copied correctly + try ( FileInputStream fis = new FileInputStream( file ) ) + { + assertThat( "Content is not equal.", + IOUtil.contentEquals( getClass().getResourceAsStream( resourceName ), fis ), is( true ) ); + } + //TODO Maybe test copy to itself like for copyFile() + } + + // forceMkdir + + @Test + public void forceMkdir() + throws Exception + { + // Tests with existing directory + FileUtils.forceMkdir( tempFolder.getRoot() ); + + // Creates test file + File testFile = new File( tempFolder.getRoot(), name.getMethodName() ); + testFile.deleteOnExit(); + testFile.createNewFile(); + assertThat( "Test file does not exist.", testFile.exists(), is( true ) ); + + // Tests with existing file + try + { + FileUtils.forceMkdir( testFile ); + fail( "Exception expected." ); + } + catch ( IOException ex ) + { + } + + testFile.delete(); + + // Tests with non-existent directory + FileUtils.forceMkdir( testFile ); + assertThat( "Directory was not created.", testFile.exists(), is( true ) ); + } + + // sizeOfDirectory + + @Test + public void sizeOfDirectory() + throws Exception + { + File file = new File( tempFolder.getRoot(), name.getMethodName() ); + + // Non-existent file + try + { + FileUtils.sizeOfDirectory( file ); + fail( "Exception expected." ); + } + catch ( IllegalArgumentException ex ) + { + } + + // Creates file + file.createNewFile(); + file.deleteOnExit(); + + // Existing file + try + { + FileUtils.sizeOfDirectory( file ); + fail( "Exception expected." ); + } + catch ( IllegalArgumentException ex ) + { + } + + // Existing directory + file.delete(); + file.mkdir(); + + assertThat( "Unexpected directory size", FileUtils.sizeOfDirectory( file ), is( (long) TEST_DIRECTORY_SIZE ) ); + } + + // copyFile + + @Test + public void copyFile1() + throws Exception + { + File destination = new File( tempFolder.getRoot(), "copy1.txt" ); + + //Thread.sleep(LAST_MODIFIED_DELAY); + //This is to slow things down so we can catch if + //the lastModified date is not ok + + FileUtils.copyFile( testFile1, destination ); + assertThat( "Check Exist", destination.exists(), is( true ) ); + assertThat( "Check Full copy", destination.length(), is( testFile1Size ) ); + /* disabled: Thread.sleep doesn't work reliantly for this case + assertTrue("Check last modified date preserved", + testFile1.lastModified() == destination.lastModified());*/ + } + + /** A time today, rounded down to the previous minute */ + private static long MODIFIED_TODAY = (System.currentTimeMillis() / TimeUnit.MINUTES.toMillis( 1 )) * TimeUnit.MINUTES.toMillis( 1 ); + + /** A time yesterday, rounded down to the previous minute */ + private static long MODIFIED_YESTERDAY = MODIFIED_TODAY - TimeUnit.DAYS.toMillis( 1 ); + + /** A time last week, rounded down to the previous minute */ + private static long MODIFIED_LAST_WEEK = MODIFIED_TODAY - TimeUnit.DAYS.toMillis( 7 ); + + @Test + public void copyFileWithNoFiltersAndNoDestination() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_YESTERDAY, + "Hello World!" + ); + File to = new File( + tempFolder.getRoot(), + "to.txt" + ); + + FileUtils.copyFile( from, to, null, ( FileUtils.FilterWrapper[] ) null); + + assertTrue( + "to.txt did not exist so should have been written", + to.lastModified() >= MODIFIED_TODAY + ); + assertFileContent( to, "Hello World!" ); + } + + @Test + public void copyFileWithNoFiltersAndOutdatedDestination() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_YESTERDAY, + "Hello World!" + ); + File to = write( + "to.txt", + MODIFIED_LAST_WEEK, + "Older content" + ); + + FileUtils.copyFile( from, to, null, ( FileUtils.FilterWrapper[] ) null); + + assertTrue( + "to.txt was outdated so should have been overwritten", + to.lastModified() >= MODIFIED_TODAY + ); + assertFileContent( to, "Hello World!" ); + } + + @Test + public void copyFileWithNoFiltersAndNewerDestination() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_LAST_WEEK, + "Hello World!" + ); + File to = write( + "to.txt", + MODIFIED_YESTERDAY, + "Older content" + ); + + FileUtils.copyFile( from, to, null, ( FileUtils.FilterWrapper[] ) null); + + assertTrue( + "to.txt was newer so should have been left alone", + to.lastModified() < MODIFIED_TODAY + ); + assertFileContent( to, "Older content" ); + } + + @Test + public void copyFileWithNoFiltersAndNewerDestinationButForcedOverwrite() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_LAST_WEEK, + "Hello World!" + ); + File to = write( + "to.txt", + MODIFIED_YESTERDAY, + "Older content" + ); + + FileUtils.copyFile( from, to, null, null, true); + + assertTrue( + "to.txt was newer but the overwrite should have been forced", + to.lastModified() >= MODIFIED_TODAY + ); + assertFileContent( to, "Hello World!" ); + } + + @Test + public void copyFileWithFilteringButNoFilters() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_YESTERDAY, + "Hello ${name}!" + ); + File to = write( + "to.txt", + MODIFIED_LAST_WEEK, + "Older content" + ); + + FileUtils.copyFile( from, to, null ); + + assertTrue( + "to.txt was outdated so should have been overwritten", + to.lastModified() >= MODIFIED_TODAY + ); + assertFileContent( to, "Hello ${name}!" ); + } + + @Test + public void copyFileWithFilteringAndNoDestination() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_YESTERDAY, + "Hello ${name}!" + ); + File to = new File( + tempFolder.getRoot(), + "to.txt" + ); + + FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ) ); + + assertTrue( + "to.txt did not exist so should have been written", + to.lastModified() >= MODIFIED_TODAY + ); + assertFileContent( to, "Hello Bob!" ); + } + + @Test + public void copyFileWithFilteringAndOutdatedDestination() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_YESTERDAY, + "Hello ${name}!" + ); + File to = write( + "to.txt", + MODIFIED_LAST_WEEK, + "Older content" + ); + + FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ) ); + + assertTrue( + "to.txt was outdated so should have been overwritten", + to.lastModified() >= MODIFIED_TODAY + ); + assertFileContent( to, "Hello Bob!" ); + } + + @Test + public void copyFileWithFilteringAndNewerDestinationButForcedOverwrite() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_LAST_WEEK, + "Hello ${name}!" + ); + File to = write( + "to.txt", + MODIFIED_YESTERDAY, + "Older content" + ); + + FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ), true ); + + assertTrue( + "to.txt was newer but the overwrite should have been forced", + to.lastModified() >= MODIFIED_TODAY + ); + assertFileContent( to, "Hello Bob!" ); + } + + @Test + public void copyFileWithFilteringAndNewerDestinationButModifiedContent() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_LAST_WEEK, + "Hello ${name}!" + ); + File to = write( + "to.txt", + MODIFIED_YESTERDAY, + "Hello Charlie!" + ); + + FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ) ); + + assertTrue( + "to.txt was outdated so should have been overwritten", + to.lastModified() >= MODIFIED_TODAY + ); + assertFileContent( to, "Hello Bob!" ); + } + + @Test + public void copyFileWithFilteringAndNewerDestinationAndMatchingContent() + throws Exception + { + File from = write( + "from.txt", + MODIFIED_LAST_WEEK, + "Hello ${name}!" + ); + File to = write( + "to.txt", + MODIFIED_YESTERDAY, + "Hello Bob!" + ); + + FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ) ); + + assertFileContent( to, "Hello Bob!" ); + assertTrue( + "to.txt content should be unchanged and have been left alone", + to.lastModified() < MODIFIED_TODAY + ); + } + + private FileUtils.FilterWrapper[] wrappers( String key, String value ) + { + final Map map = new HashMap<>(); + map.put( key, value ); + return new FileUtils.FilterWrapper[] + { + new FileUtils.FilterWrapper() + { + @Override + public Reader getReader( Reader reader ) + { + return new InterpolationFilterReader( reader, map ); + } + } + }; + } + + private File write( @Nonnull String name, long lastModified, @Nonnull String text) throws IOException + { + final File file = new File( tempFolder.getRoot(), name ); + try ( final Writer writer = new FileWriter( file ) ) { + writer.write(text); + } + assertTrue( file.setLastModified( lastModified ) ); + assertEquals( "Failed to set lastModified date on " + file.getPath(), lastModified, file.lastModified() ); + return file; + } + + private static void assertFileContent( @Nonnull File file, @Nonnull String expected ) throws IOException + { + try ( Reader in = new FileReader( file )) + { + assertEquals( + "Expected " + file.getPath() + " to contain: " + expected, + expected, + IOUtil.toString( in ) + ); + } + } + + @Test + public void copyFileThatIsSymlink() + throws Exception + { + assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) ); + + File destination = new File( tempFolder.getRoot(), "symCopy.txt" ); + + File testDir = SymlinkTestSetup.createStandardSymlinkTestDir( new File( "target/test/symlinkCopy" ) ); + + FileUtils.copyFile( new File( testDir, "symR" ), destination ); + + assertTrue( Files.isSymbolicLink( destination.toPath() ) ); + } + + + @Test + public void deleteFile() + throws Exception + { + File destination = new File( tempFolder.getRoot(), "copy1.txt" ); + FileUtils.copyFile( testFile1, destination ); + FileUtils.delete( destination ); + assertThat( "Check Exist", destination.exists(), is( false ) ); + } + + @Test(expected = IOException.class) + public void deleteFileNofile() + throws Exception + { + File destination = new File( "abc/cde" ); + FileUtils.delete( destination ); + } + + @Test + public void deleteFileLegacy() + throws Exception + { + File destination = new File( tempFolder.getRoot(), "copy1.txt" ); + FileUtils.copyFile( testFile1, destination ); + assertTrue( FileUtils.deleteLegacyStyle( destination ) ); + } + + @Test + public void deleteFileLegacyNofile() + throws Exception + { + File destination = new File( "abc/cde" ); + assertFalse( FileUtils.deleteLegacyStyle( destination ) ); + } + + @Test + public void copyFileWithPermissions() + throws Exception + { + File source = new File( "src/test/resources/executable" ); + source.setExecutable( true ); + assumeThat( "Need an existing file to copy", source.exists(), is( true ) ); + assumeThat( "Need an executable file to copy", source.canExecute(), is( true ) ); + + File destination = new File( tempFolder.getRoot(), "executable-copy" ); + + FileUtils.copyFile( source, destination ); + + assertThat( "destination not exists: " + destination.getAbsolutePath() + + ", directory content: " + Arrays.asList( destination.getParentFile().list() ), + Files.exists( destination.toPath() ), is( true ) ); + + assertThat( "Check copy executable", destination.canExecute(), is( true ) ); + } + + @Test + public void copyFile2() + throws Exception + { + File destination = new File( tempFolder.getRoot(), "copy2.txt" ); + + //Thread.sleep(LAST_MODIFIED_DELAY); + //This is to slow things down so we can catch if + //the lastModified date is not ok + + FileUtils.copyFile( testFile1, destination ); + assertThat( "Check Exist", destination.exists(), is( true ) ); + assertThat( "Check Full copy", destination.length(), is( testFile2Size ) ); + /* disabled: Thread.sleep doesn't work reliably for this case + assertTrue("Check last modified date preserved", + testFile1.lastModified() == destination.lastModified());*/ + } + + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void copyToSelf() + throws Exception + { + File destination = new File( tempFolder.getRoot(), "copy3.txt" ); + //Prepare a test file + FileUtils.copyFile( testFile1, destination ); + + FileUtils.copyFile( destination, destination ); + } + + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void copyDirectoryToDirectory_NonExistingDest() + throws Exception + { + createFile( testFile1, 1234 ); + createFile( testFile2, 4321 ); + File srcDir = tempFolder.getRoot(); + File subDir = new File( srcDir, "sub" ); + subDir.mkdir(); + File subFile = new File( subDir, "A.txt" ); + FileUtils.fileWrite( subFile, "UTF8", "HELLO WORLD" ); + File destDir = new File( System.getProperty( "java.io.tmpdir" ), "tmp-FileUtilsTestCase" ); + FileUtils.deleteDirectory( destDir ); + File actualDestDir = new File( destDir, srcDir.getName() ); + + FileUtils.copyDirectory( srcDir, destDir ); + + assertThat( "Check exists", destDir.exists(), is( true ) ); + assertThat( "Check exists", actualDestDir.exists(), is( true ) ); + assertThat( "Check size", FileUtils.sizeOfDirectory( actualDestDir ), + is( FileUtils.sizeOfDirectory( srcDir ) ) ); + assertThat( new File( actualDestDir, "sub/A.txt" ).exists(), is( true ) ); + FileUtils.deleteDirectory( destDir ); + } + + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void copyDirectoryToNonExistingDest() + throws Exception + { + createFile( testFile1, 1234 ); + createFile( testFile2, 4321 ); + File srcDir = tempFolder.getRoot(); + File subDir = new File( srcDir, "sub" ); + subDir.mkdir(); + File subFile = new File( subDir, "A.txt" ); + FileUtils.fileWrite( subFile, "UTF8", "HELLO WORLD" ); + File destDir = new File( System.getProperty( "java.io.tmpdir" ), "tmp-FileUtilsTestCase" ); + FileUtils.deleteDirectory( destDir ); + + FileUtils.copyDirectory( srcDir, destDir ); + + assertThat( "Check exists", destDir.exists(), is( true ) ); + assertThat( "Check size", FileUtils.sizeOfDirectory( destDir ), is( FileUtils.sizeOfDirectory( srcDir ) ) ); + assertThat( new File( destDir, "sub/A.txt" ).exists(), is( true ) ); + FileUtils.deleteDirectory( destDir ); + } + + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void copyDirectoryToExistingDest() + throws Exception + { + createFile( testFile1, 1234 ); + createFile( testFile2, 4321 ); + File srcDir = tempFolder.getRoot(); + File subDir = new File( srcDir, "sub" ); + subDir.mkdir(); + File subFile = new File( subDir, "A.txt" ); + FileUtils.fileWrite( subFile, "UTF8", "HELLO WORLD" ); + File destDir = new File( System.getProperty( "java.io.tmpdir" ), "tmp-FileUtilsTestCase" ); + FileUtils.deleteDirectory( destDir ); + destDir.mkdirs(); + + FileUtils.copyDirectory( srcDir, destDir ); + + assertThat( FileUtils.sizeOfDirectory( destDir ), is( FileUtils.sizeOfDirectory( srcDir ) ) ); + assertThat( new File( destDir, "sub/A.txt" ).exists(), is( true ) ); + } + + /** + * Test for IO-141 + */ + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void copyDirectoryToChild() + throws Exception + { + File grandParentDir = new File( tempFolder.getRoot(), "grandparent" ); + File parentDir = new File( grandParentDir, "parent" ); + File childDir = new File( parentDir, "child" ); + createFilesForTestCopyDirectory( grandParentDir, parentDir, childDir ); + + long expectedCount = + FileUtils.getFileAndDirectoryNames( grandParentDir, null, null, true, true, true, true ).size() + + FileUtils.getFileAndDirectoryNames( parentDir, null, null, true, true, true, true ).size(); + long expectedSize = FileUtils.sizeOfDirectory( grandParentDir ) + FileUtils.sizeOfDirectory( parentDir ); + FileUtils.copyDirectory( parentDir, childDir ); + assertThat( + 1L * FileUtils.getFileAndDirectoryNames( grandParentDir, null, null, true, true, true, true ).size(), + is( expectedCount ) ); + assertThat( FileUtils.sizeOfDirectory( grandParentDir ), is( expectedSize ) ); + } + + /** + * Test for IO-141 + */ + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void copyDirectoryToGrandChild() + throws Exception + { + File grandParentDir = new File( tempFolder.getRoot(), "grandparent" ); + File parentDir = new File( grandParentDir, "parent" ); + File childDir = new File( parentDir, "child" ); + createFilesForTestCopyDirectory( grandParentDir, parentDir, childDir ); + + long expectedCount = + ( FileUtils.getFileAndDirectoryNames( grandParentDir, null, null, true, true, true, true ).size() * 2 ); + long expectedSize = ( FileUtils.sizeOfDirectory( grandParentDir ) * 2 ); + FileUtils.copyDirectory( grandParentDir, childDir ); + assertThat( + 1L * FileUtils.getFileAndDirectoryNames( grandParentDir, null, null, true, true, true, true ).size(), + is( expectedCount ) ); + assertThat( FileUtils.sizeOfDirectory( grandParentDir ), is( expectedSize ) ); + } + + /** + * Test for IO-217 FileUtils.copyDirectoryToDirectory makes infinite loops + */ + @Test + public void copyDirectoryToItself() + throws Exception + { + File dir = new File( tempFolder.getRoot(), "itself" ); + dir.mkdirs(); + FileUtils.copyDirectory( dir, dir ); + assertThat( FileUtils.getFileAndDirectoryNames( dir, null, null, true, true, true, true ).size(), is( 1 ) ); + } + + private void createFilesForTestCopyDirectory( File grandParentDir, File parentDir, File childDir ) + throws Exception + { + File childDir2 = new File( parentDir, "child2" ); + File grandChildDir = new File( childDir, "grandChild" ); + File grandChild2Dir = new File( childDir2, "grandChild2" ); + File file1 = new File( grandParentDir, "file1.txt" ); + File file2 = new File( parentDir, "file2.txt" ); + File file3 = new File( childDir, "file3.txt" ); + File file4 = new File( childDir2, "file4.txt" ); + File file5 = new File( grandChildDir, "file5.txt" ); + File file6 = new File( grandChild2Dir, "file6.txt" ); + FileUtils.deleteDirectory( grandParentDir ); + grandChildDir.mkdirs(); + grandChild2Dir.mkdirs(); + FileUtils.fileWrite( file1, "UTF8", "File 1 in grandparent" ); + FileUtils.fileWrite( file2, "UTF8", "File 2 in parent" ); + FileUtils.fileWrite( file3, "UTF8", "File 3 in child" ); + FileUtils.fileWrite( file4, "UTF8", "File 4 in child2" ); + FileUtils.fileWrite( file5, "UTF8", "File 5 in grandChild" ); + FileUtils.fileWrite( file6, "UTF8", "File 6 in grandChild2" ); + } + + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void copyDirectoryErrors() + throws Exception + { + try + { + FileUtils.copyDirectory( null, null ); + fail(); + } + catch ( NullPointerException ex ) + { + } + try + { + FileUtils.copyDirectory( new File( "a" ), null ); + fail(); + } + catch ( NullPointerException ex ) + { + } + try + { + FileUtils.copyDirectory( null, new File( "a" ) ); + fail(); + } + catch ( NullPointerException ex ) + { + } + try + { + FileUtils.copyDirectory( new File( "doesnt-exist" ), new File( "a" ) ); + fail(); + } + catch ( IOException ex ) + { + } + try + { + FileUtils.copyDirectory( testFile1, new File( "a" ) ); + fail(); + } + catch ( IOException ex ) + { + } + try + { + FileUtils.copyDirectory( tempFolder.getRoot(), testFile1 ); + fail(); + } + catch ( IOException ex ) + { + } + try + { + FileUtils.copyDirectory( tempFolder.getRoot(), tempFolder.getRoot() ); + fail(); + } + catch ( IOException ex ) + { + } + } + + // forceDelete + + @Test + public void forceDeleteAFile1() + throws Exception + { + File destination = new File( tempFolder.getRoot(), "copy1.txt" ); + destination.createNewFile(); + assertThat( "Copy1.txt doesn't exist to delete", destination.exists(), is( true ) ); + FileUtils.forceDelete( destination ); + assertThat( "Check No Exist", !destination.exists(), is( true ) ); + } + + @Test + public void forceDeleteAFile2() + throws Exception + { + File destination = new File( tempFolder.getRoot(), "copy2.txt" ); + destination.createNewFile(); + assertThat( "Copy2.txt doesn't exist to delete", destination.exists(), is( true ) ); + FileUtils.forceDelete( destination ); + assertThat( "Check No Exist", !destination.exists(), is( true ) ); + } + + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void forceDeleteAFile3() + throws Exception + { + File destination = new File( tempFolder.getRoot(), "no_such_file" ); + assertThat( "Check No Exist", !destination.exists(), is( true ) ); + try + { + FileUtils.forceDelete( destination ); + fail( "Should generate FileNotFoundException" ); + } + catch ( FileNotFoundException ignored ) + { + } + } + + // copyFileToDirectory + + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void copyFile1ToDir() + throws Exception + { + File directory = new File( tempFolder.getRoot(), "subdir" ); + if ( !directory.exists() ) + { + directory.mkdirs(); + } + File destination = new File( directory, testFile1.getName() ); + + //Thread.sleep(LAST_MODIFIED_DELAY); + //This is to slow things down so we can catch if + //the lastModified date is not ok + + FileUtils.copyFileToDirectory( testFile1, directory ); + assertThat( "Check Exist", destination.exists(), is( true ) ); + assertThat( "Check Full copy", destination.length(), is( testFile1Size ) ); + /* disabled: Thread.sleep doesn't work reliantly for this case + assertTrue("Check last modified date preserved", + testFile1.lastModified() == destination.lastModified());*/ + + try + { + FileUtils.copyFileToDirectory( destination, directory ); + fail( "Should not be able to copy a file into the same directory as itself" ); + } + catch ( IOException ioe ) + { + //we want that, cannot copy to the same directory as the original file + } + } + + @Test + public void copyFile2ToDir() + throws Exception + { + File directory = new File( tempFolder.getRoot(), "subdir" ); + if ( !directory.exists() ) + { + directory.mkdirs(); + } + File destination = new File( directory, testFile1.getName() ); + + //Thread.sleep(LAST_MODIFIED_DELAY); + //This is to slow things down so we can catch if + //the lastModified date is not ok + + FileUtils.copyFileToDirectory( testFile1, directory ); + assertThat( "Check Exist", destination.exists(), is( true ) ); + assertThat( "Check Full copy", destination.length(), is( testFile2Size ) ); + /* disabled: Thread.sleep doesn't work reliantly for this case + assertTrue("Check last modified date preserved", + testFile1.lastModified() == destination.lastModified());*/ + } + + // forceDelete + + @Test + public void forceDeleteDir() + throws Exception + { + File testDirectory = tempFolder.newFolder( name.getMethodName() ); + FileUtils.forceDelete( testDirectory.getParentFile() ); + assertThat( "Check No Exist", !testDirectory.getParentFile().exists(), is( true ) ); + } + + /** + * Test the FileUtils implementation. + */ + @Test + public void fileUtils() + throws Exception + { + // Loads file from classpath + File file1 = new File( tempFolder.getRoot(), "test.txt" ); + String filename = file1.getAbsolutePath(); + + //Create test file on-the-fly (used to be in CVS) + try ( OutputStream out = new java.io.FileOutputStream( file1 ) ) + { + out.write( "This is a test".getBytes( "UTF-8" ) ); + } + + File file2 = new File( tempFolder.getRoot(), "test2.txt" ); + + FileUtils.fileWrite( file2, "UTF-8", filename ); + assertThat( file2.exists(), is( true ) ); + assertThat( file2.length() > 0, is( true ) ); + + String file2contents = FileUtils.fileRead( file2, "UTF-8" ); + assertThat( "Second file's contents correct", filename.equals( file2contents ), is( true ) ); + + assertThat( file2.delete(), is( true ) ); + + String contents = FileUtils.fileRead( new File( filename ), "UTF-8" ); + assertThat( "FileUtils.fileRead()", contents.equals( "This is a test" ), is( true ) ); + + } + + @Test + public void fileReadWithDefaultEncoding() + throws Exception + { + File file = new File( tempFolder.getRoot(), "read.obj" ); + FileOutputStream out = new FileOutputStream( file ); + byte[] text = "Hello /u1234".getBytes(); + out.write( text ); + out.close(); + + String data = FileUtils.fileRead( file ); + assertThat( data, is( "Hello /u1234" ) ); + } + + @Test + public void fileReadWithEncoding() + throws Exception + { + File file = new File( tempFolder.getRoot(), "read.obj" ); + FileOutputStream out = new FileOutputStream( file ); + byte[] text = "Hello /u1234".getBytes( "UTF8" ); + out.write( text ); + out.close(); + + String data = FileUtils.fileRead( file, "UTF8" ); + assertThat( data, is( "Hello /u1234" ) ); + } + + @Test + @Ignore( "Commons test case that is failing for plexus" ) + public void readLines() + throws Exception + { + File file = FileTestHelper.newFile( tempFolder, "lines.txt" ); + try + { + String[] data = new String[]{ "hello", "/u1234", "", "this is", "some text" }; + FileTestHelper.createLineBasedFile( file, data ); + + List lines = FileUtils.loadFile( file ); + assertThat( lines, is( Arrays.asList( data ) ) ); + } + finally + { + deleteFile( file ); + } + } + + @Test + public void writeStringToFile1() + throws Exception + { + File file = new File( tempFolder.getRoot(), "write.txt" ); + FileUtils.fileWrite( file, "UTF8", "Hello /u1234" ); + byte[] text = "Hello /u1234".getBytes( "UTF8" ); + assertEqualContent( text, file ); + } + + @Test + public void writeStringToFile2() + throws Exception + { + File file = new File( tempFolder.getRoot(), "write.txt" ); + FileUtils.fileWrite( file, null, "Hello /u1234" ); + byte[] text = "Hello /u1234".getBytes(); + assertEqualContent( text, file ); + } + + @Test + public void writeCharSequence1() + throws Exception + { + File file = new File( tempFolder.getRoot(), "write.txt" ); + FileUtils.fileWrite( file, "UTF8", "Hello /u1234" ); + byte[] text = "Hello /u1234".getBytes( "UTF8" ); + assertEqualContent( text, file ); + } + + @Test + public void writeCharSequence2() + throws Exception + { + File file = new File( tempFolder.getRoot(), "write.txt" ); + FileUtils.fileWrite( file, null, "Hello /u1234" ); + byte[] text = "Hello /u1234".getBytes(); + assertEqualContent( text, file ); + } + + + @Test + public void writeStringToFileWithEncoding_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() + throws Exception + { + File file = FileTestHelper.newFile( tempFolder, "lines.txt" ); + FileUtils.fileWrite( file, null, "This line was there before you..." ); + + FileUtils.fileAppend( file.getAbsolutePath(), "this is brand new data" ); + + String expected = "This line was there before you..." + "this is brand new data"; + String actual = FileUtils.fileRead( file ); + assertThat( actual, is( expected ) ); + } + + @Test + public void writeStringToFile_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() + throws Exception + { + File file = FileTestHelper.newFile( tempFolder, "lines.txt" ); + FileUtils.fileWrite( file, null, "This line was there before you..." ); + + FileUtils.fileAppend( file.getAbsolutePath(), "this is brand new data" ); + + String expected = "This line was there before you..." + "this is brand new data"; + String actual = FileUtils.fileRead( file ); + assertThat( actual, is( expected ) ); + } + + @Test + public void writeStringArrayToFile() + throws Exception + { + File file = new File( tempFolder.getRoot(), "writeArray.txt" ); + FileUtils.fileWriteArray( file, new String[]{"line1", "line2", "line3"} ); + + byte[] text = "line1\nline2\nline3".getBytes( "UTF8" ); + assertEqualContent( text, file ); + } + + @Test + public void writeStringArrayToFileWithEncoding() + throws Exception + { + File file = new File( tempFolder.getRoot(), "writeArray.txt" ); + FileUtils.fileWriteArray( file, "UTF8", new String[]{"line1", "line2", "line3"} ); + + byte[] text = "line1\nline2\nline3".getBytes( "UTF8" ); + assertEqualContent( text, file ); + } + + + @Test + public void writeWithEncoding_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() + throws Exception + { + File file = FileTestHelper.newFile( tempFolder, "lines.txt" ); + FileUtils.fileWrite( file, "UTF-8", "This line was there before you..." ); + + FileUtils.fileAppend( file.getAbsolutePath(), "UTF-8", "this is brand new data" ); + + String expected = "This line was there before you..." + "this is brand new data"; + String actual = FileUtils.fileRead( file ); + assertThat( actual, is( expected ) ); + } + + @Test + public void write_WithAppendOptionTrue_ShouldNotDeletePreviousFileLines() + throws Exception + { + File file = FileTestHelper.newFile( tempFolder, "lines.txt" ); + FileUtils.fileWrite( file, null, "This line was there before you..." ); + + FileUtils.fileAppend( file.getAbsolutePath(), "this is brand new data" ); + + String expected = "This line was there before you..." + "this is brand new data"; + String actual = FileUtils.fileRead( file ); + assertThat( actual, is( expected ) ); + } + + @Test( expected = NullPointerException.class ) + public void blowUpOnNull() + throws IOException + { + FileUtils.deleteDirectory( (File) null ); + } + + @Test + public void deleteQuietlyDir() + throws IOException + { + File testDirectory = new File( tempFolder.getRoot(), "testDeleteQuietlyDir" ); + File testFile = new File( testDirectory, "testDeleteQuietlyFile" ); + testDirectory.mkdirs(); + createFile( testFile, 0 ); + + assertThat( testDirectory.exists(), is( true ) ); + assertThat( testFile.exists(), is( true ) ); + FileUtils.deleteDirectory( testDirectory ); + assertThat( "Check No Exist", testDirectory.exists(), is( false ) ); + assertThat( "Check No Exist", testFile.exists(), is( false ) ); + } + + @Test + public void deleteQuietlyFile() + throws IOException + { + File testFile = new File( tempFolder.getRoot(), "testDeleteQuietlyFile" ); + createFile( testFile, 0 ); + + assertThat( testFile.exists(), is( true ) ); + FileUtils.deleteDirectory( testFile ); + assertThat( "Check No Exist", testFile.exists(), is( false ) ); + } + + @Test + public void deleteQuietlyNonExistent() + throws IOException + { + File testFile = new File( tempFolder.getRoot(), "testDeleteQuietlyNonExistent" ); + assertThat( testFile.exists(), is( false ) ); + + FileUtils.deleteDirectory( testFile ); + } + + + //// getDefaultExcludes + + @Test + public void getDefaultExcludes() + throws Exception + { + assertThat( Arrays.asList( FileUtils.getDefaultExcludes() ), hasItems( MINIMUM_DEFAULT_EXCLUDES ) ); + } + + + //// getDefaultExcludesAsList + + @Test + public void getDefaultExcludesAsList() + throws Exception + { + assertThat( FileUtils.getDefaultExcludesAsList(), hasItems( MINIMUM_DEFAULT_EXCLUDES ) ); + } + + + //// getDefaultExcludesAsString + + @Test + public void getDefaultExcludesAsString() + throws Exception + { + assertThat( new HashSet( Arrays.asList( FileUtils.getDefaultExcludesAsString().split( "," ) ) ), + hasItems( MINIMUM_DEFAULT_EXCLUDES ) ); + } + + + + //// dirname(String) + + @SuppressWarnings("ConstantConditions") + @Test( expected = NullPointerException.class ) + public void nlowUpOnDirnameNull() + throws Exception + { + FileUtils.dirname( null ); + } + + @Test + public void dirnameEmpty() + throws Exception + { + assertThat( FileUtils.dirname( "" ), is( "" ) ); + } + + @Test + public void dirnameFilename() + throws Exception + { + assertThat( FileUtils.dirname( "foo.bar.txt" ), is( "" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void dirnameWindowsRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.dirname( "C:\\foo.bar.txt" ), is( "" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void dirnameWindowsNonRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.dirname( "C:\\test\\foo.bar.txt" ), is( "" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void dirnameUnixRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.dirname( "/foo.bar.txt" ), is( "" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void dirnameUnixNonRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.dirname( "/test/foo.bar.txt" ), is( "" ) ); + } + + @Test + public void dirnameWindowsRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.dirname( "C:\\foo.bar.txt" ), is( "C:" ) ); + } + + @Test + public void dirnameWindowsNonRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.dirname( "C:\\test\\foo.bar.txt" ), is( "C:\\test" ) ); + } + + @Test + public void dirnameUnixRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.dirname( "/foo.bar.txt" ), is( "" ) ); + } + + @Test + public void dirnameUnixNonRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.dirname( "/test/foo.bar.txt" ), is( "/test" ) ); + } + + //// filename(String) + + @SuppressWarnings("ConstantConditions") + @Test( expected = NullPointerException.class ) + public void blowUpOnFilenameNull() + throws Exception + { + FileUtils.filename( null ); + } + + @Test + public void filenameEmpty() + throws Exception + { + assertThat( FileUtils.filename( "" ), is( "" ) ); + } + + @Test + public void filenameFilename() + throws Exception + { + assertThat( FileUtils.filename( "foo.bar.txt" ), is( "foo.bar.txt" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void filenameWindowsRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.filename( "C:\\foo.bar.txt" ), is( "C:\\foo.bar.txt" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void filenameWindowsNonRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.filename( "C:\\test\\foo.bar.txt" ), is( "C:\\test\\foo.bar.txt" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void filenameUnixRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.filename( "/foo.bar.txt" ), is( "/foo.bar.txt" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void filenameUnixNonRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.filename( "/test/foo.bar.txt" ), is( "/test/foo.bar.txt" ) ); + } + + @Test + public void filenameWindowsRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.filename( "C:\\foo.bar.txt" ), is( "foo.bar.txt" ) ); + } + + @Test + public void filenameWindowsNonRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.filename( "C:\\test\\foo.bar.txt" ), is( "foo.bar.txt" ) ); + } + + @Test + public void filenameUnixRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.filename( "/foo.bar.txt" ), is( "foo.bar.txt" ) ); + } + + @Test + public void filenameUnixNonRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.filename( "/test/foo.bar.txt" ), is( "foo.bar.txt" ) ); + } + + //// extension(String) + + @SuppressWarnings("ConstantConditions") + @Test( expected = NullPointerException.class ) + public void blowUpOnNullExtension() + throws Exception + { + FileUtils.extension( null ); + } + + @Test + public void extensionEmpty() + throws Exception + { + assertThat( FileUtils.extension( "" ), is( "" ) ); + } + + @Test + public void extensionFileName() + throws Exception + { + assertThat( FileUtils.extension( "foo.bar.txt" ), is( "txt" ) ); + } + + @Test + public void extensionFileNameNoExtension() + throws Exception + { + assertThat( FileUtils.extension( "foo_bar_txt" ), is( "" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void extensionWindowsRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.extension( "C:\\foo.bar.txt" ), is( "txt" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void extensionWindowsNonRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.extension( "C:\\test\\foo.bar.txt" ), is( "txt" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void extensionUnixRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.extension( "/foo.bar.txt" ), is( "txt" ) ); + } + + @Test + //X @ReproducesPlexusBug( "assumes that the path is a local path" ) + public void extensionUnixNonRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.extension( "/test/foo.bar.txt" ), is( "txt" ) ); + } + + @Test + public void extensionWindowsRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.extension( "C:\\foo.bar.txt" ), is( "txt" ) ); + } + + @Test + public void extensionWindowsNonRootPathOnWindows() + throws Exception + { + assumeThat( File.separatorChar, is( '\\' ) ); + assertThat( FileUtils.extension( "C:\\test\\foo.bar.txt" ), is( "txt" ) ); + } + + @Test + @Ignore("Wait until we can run with assembly 2.5 which will support symlinks properly") + public void isASymbolicLink() + throws IOException + { + // This testcase will pass when running under java7 or higher + assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) ); + + File file = new File( "src/test/resources/symlinks/src/symDir" ); + assertTrue(FileUtils.isSymbolicLink(file )); + } + + @Test + @Ignore("Wait until we can run with assembly 2.5 which will support symlinks properly") + public void notASymbolicLink() + throws IOException + { + File file = new File( "src/test/resources/symlinks/src/" ); + assertFalse(FileUtils.isSymbolicLink(file )); + } + + @Test + public void extensionUnixRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.extension( "/foo.bar.txt" ), is( "txt" ) ); + } + + @Test + public void extensionUnixNonRootPathOnUnix() + throws Exception + { + assumeThat( File.separatorChar, is( '/' ) ); + assertThat( FileUtils.extension( "/test/foo.bar.txt" ), is( "txt" ) ); + } + + @Test + public void createAndReadSymlink() + throws Exception + { + assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) ); + + File file = new File( "target/fzz" ); + FileUtils.createSymbolicLink( file, new File("../target") ); + + final File file1 = Files.readSymbolicLink( file.toPath() ).toFile(); + assertEquals( "target", file1.getName() ); + Files.delete( file.toPath() ); + } + + @Test + public void createSymbolicLinkWithDifferentTargetOverwritesSymlink() + throws Exception + { + assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) ); + + // Arrange + + final File symlink1 = new File( tempFolder.getRoot(), "symlink" ); + + FileUtils.createSymbolicLink( symlink1, testFile1 ); + + // Act + + final File symlink2 = FileUtils.createSymbolicLink( symlink1, testFile2 ); + + // Assert + + assertThat( + Files.readSymbolicLink( symlink2.toPath() ).toFile(), + CoreMatchers.equalTo( testFile2 ) + ); + } + + //// constants for testing + + private static final String[] MINIMUM_DEFAULT_EXCLUDES = { + // Miscellaneous typical temporary files + "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", + + // CVS + "**/CVS", "**/CVS/**", "**/.cvsignore", + + // Subversion + "**/.svn", "**/.svn/**", + + // Arch + "**/.arch-ids", "**/.arch-ids/**", + + //Bazaar + "**/.bzr", "**/.bzr/**", + + //SurroundSCM + "**/.MySCMServerInfo", + + // Mac + "**/.DS_Store", + + // Serena Dimensions Version 10 + "**/.metadata", "**/.metadata/**", + + // Mercurial + "**/.hg", "**/.hg/**", + + // git + "**/.git", "**/.git/**", + + // BitKeeper + "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**", + + // darcs + "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" }; + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/IOUtilTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/IOUtilTest.java new file mode 100644 index 000000000..a8b6b31ab --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/IOUtilTest.java @@ -0,0 +1,3043 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import org.junit.Test; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +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.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@SuppressWarnings( "deprecation" ) +public class IOUtilTest +{ + + private static final long INFINITE_LOOP_TIMEOUT = 500; + + @Test + public void closeReaderWithNull() + throws Exception + { + IOUtil.close( (Reader) null ); + } + + @Test + public void closeWriterWithNull() + throws Exception + { + IOUtil.close( (Writer) null ); + } + + @Test + public void closeInputStreamWithNull() + throws Exception + { + IOUtil.close( nullInputStream() ); + } + + @Test + public void closeOutputStreamWithNull() + throws Exception + { + IOUtil.close( nullOutputStream() ); + } + + @Test + public void closeReaderWithIOE() + throws Exception + { + IOUtil.close( new BufferedReader( new StringReader( emptyString() ) ) + { + @Override + public void close() + throws IOException + { + super.close(); + throw new IOException( "don't bomb out" ); + } + } ); + } + + @Test + public void closeWriterWithIOE() + throws Exception + { + IOUtil.close( new BufferedWriter( new StringWriter() ) + { + @Override + public void close() + throws IOException + { + super.close(); + throw new IOException( "don't bomb out" ); + } + } ); + } + + @Test + public void closeInputStreamWithIOE() + throws Exception + { + IOUtil.close( new BufferedInputStream( emptyInputStream() ) + { + @Override + public void close() + throws IOException + { + super.close(); + throw new IOException( "don't bomb out" ); + } + } ); + } + + @Test + public void closeOutputStreamWithIOE() + throws Exception + { + IOUtil.close( new BufferedOutputStream( new ByteArrayOutputStream() ) + { + @Override + public void close() + throws IOException + { + super.close(); + throw new IOException( "don't bomb out" ); + } + } ); + } + + @Test + public void closeReaderCloses() + throws Exception + { + final AtomicBoolean closed = new AtomicBoolean( false ); + IOUtil.close( new BufferedReader( new StringReader( emptyString() ) ) + { + @Override + public void close() + throws IOException + { + closed.set( true ); + super.close(); + } + } ); + assertThat( closed.get(), is( true ) ); + } + + @Test + public void closeWriterCloses() + throws Exception + { + final AtomicBoolean closed = new AtomicBoolean( false ); + IOUtil.close( new BufferedWriter( new StringWriter() ) + { + @Override + public void close() + throws IOException + { + closed.set( true ); + super.close(); + } + } ); + assertThat( closed.get(), is( true ) ); + } + + @Test + public void closeInputStreamCloses() + throws Exception + { + final AtomicBoolean closed = new AtomicBoolean( false ); + IOUtil.close( new BufferedInputStream( emptyInputStream() ) + { + @Override + public void close() + throws IOException + { + closed.set( true ); + super.close(); + } + } ); + assertThat( closed.get(), is( true ) ); + } + + @Test + public void closeOutputStreamCloses() + throws Exception + { + final AtomicBoolean closed = new AtomicBoolean( false ); + IOUtil.close( new BufferedOutputStream( new ByteArrayOutputStream() ) + { + @Override + public void close() + throws IOException + { + closed.set( true ); + super.close(); + } + } ); + assertThat( closed.get(), is( true ) ); + } + + @Test + public void toByteArrayFromString() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( probe ), is( probe.getBytes() ) ); + } + + @Test + public void toByteArrayFromReader() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( new StringReader( probe ) ), is( probe.getBytes() ) ); + } + + @Test + public void toByteArrayFromInputStream() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( new DontCloseByteArrayInputStream( IOUtil.toByteArray( probe ) ) ), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toByteArrayNullString() + throws Exception + { + IOUtil.toByteArray( (String) null ); + } + + @Test( expected = NullPointerException.class ) + public void toByteArrayNullReader() + throws Exception + { + IOUtil.toByteArray( (Reader) null ); + } + + @Test( expected = NullPointerException.class ) + public void toByteArrayNullInputStream() + throws Exception + { + IOUtil.toByteArray( nullInputStream() ); + } + + @Test( expected = IOException.class ) + public void contentEqualNullNull() + throws Exception + { + IOUtil.contentEquals( null, null ); + } + + @Test( expected = IOException.class ) + public void contentEqualNonNullNull() + throws Exception + { + IOUtil.contentEquals( new DontCloseByteArrayInputStream( emptyByteArray() ), null ); + } + + @Test( expected = IOException.class ) + public void contentEqualNullNonNull() + throws Exception + { + IOUtil.contentEquals( new DontCloseByteArrayInputStream( emptyByteArray() ), null ); + } + + @Test + public void contentEqualEmptyEmpty() + throws Exception + { + assertThat( IOUtil.contentEquals( new DontCloseByteArrayInputStream( emptyByteArray() ), + new DontCloseByteArrayInputStream( emptyByteArray() ) ), is( true ) ); + } + + @Test + public void contentEqualNonEmptyEmpty() + throws Exception + { + assertThat( IOUtil.contentEquals( new DontCloseByteArrayInputStream( new byte[1] ), + new DontCloseByteArrayInputStream( emptyByteArray() ) ), is( false ) ); + } + + @Test + public void contentEqualEmptyNonEmpty() + throws Exception + { + assertThat( IOUtil.contentEquals( new DontCloseByteArrayInputStream( emptyByteArray() ), + new DontCloseByteArrayInputStream( new byte[1] ) ), is( false ) ); + } + + @Test + public void contentEqualNonEmptyNonEmpty() + throws Exception + { + assertThat( IOUtil.contentEquals( new DontCloseByteArrayInputStream( new byte[1] ), + new DontCloseByteArrayInputStream( new byte[1] ) ), is( true ) ); + } + + @Test + public void contentEqualMostlySame() + throws Exception + { + assertThat( IOUtil.contentEquals( new DontCloseByteArrayInputStream( new byte[]{ 1, 2, 3, 4, 5, 6 } ), + new DontCloseByteArrayInputStream( new byte[]{ 1, 2, 3, 4, 5, 7 } ) ), + is( false ) ); + } + + @Test + public void contentEqualLargeSame() + throws Exception + { + assertThat( IOUtil.contentEquals( new DontCloseByteArrayInputStream( new byte[8192] ), + new DontCloseByteArrayInputStream( new byte[8192] ) ), is( true ) ); + } + + @Test + public void contentEqualLargeDifferent() + throws Exception + { + byte[] buf = new byte[8192]; + buf[8191] = 1; + assertThat( IOUtil.contentEquals( new DontCloseByteArrayInputStream( new byte[8192] ), + new DontCloseByteArrayInputStream( buf ) ), is( false ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArray() + throws Exception + { + IOUtil.toString( nullByteArray() ); + } + + @Test + public void toStringEmptyByteArray() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray() ), is( emptyString() ) ); + } + + @Test + public void toStringByteArray() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes() ).getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArrayNegBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringEmptyByteArrayNegBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), -1 ), is( emptyString() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringByteArrayNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes(), -1 ), is( probe ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringNullByteArrayZeroBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), 0 ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArrayPosBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), 1 ); + } + + @Test + public void toStringEmptyByteArrayPosBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), 1 ), is( emptyString() ) ); + } + + @Test + public void toStringByteArrayPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes(), 1 ).getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArrayNullEncoding() + throws Exception + { + IOUtil.toString( nullByteArray(), null ); + } + + @Test( expected = NullPointerException.class ) + public void toStringEmptyByteArrayNullEncoding() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), null ), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringByteArrayNullEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes(), null ).getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArrayJunkEncoding() + throws Exception + { + IOUtil.toString( nullByteArray(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void toStringEmptyByteArrayJunkEncoding() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), "junk" ), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void toStringByteArrayJunkEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes(), "junk" ).getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArrayValidEncoding() + throws Exception + { + IOUtil.toString( nullByteArray(), "utf-16" ); + } + + @Test + public void toStringEmptyByteArrayValidEncoding() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), "utf-16" ), is( emptyString() ) ); + } + + @Test + public void toStringByteArrayValidEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes( "utf-16" ), "utf-16" ).getBytes( "utf-8" ), + is( probe.getBytes( "utf-8" ) ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArrayNullEncodingNegBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void toStringEmptyByteArrayNullEncodingNegBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), null, -1 ), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringByteArrayNullEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes(), null, -1 ).getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArrayJunkEncodingNegBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), "junk", -1 ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void toStringEmptyByteArrayJunkEncodingNegBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), "junk", -1 ), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void toStringByteArrayJunkEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes(), "junk", -1 ).getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullByteArrayValidEncodingNegBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), "utf-16", -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringEmptyByteArrayValidEncodingNegBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), "utf-16", -1 ), is( emptyString() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringByteArrayValidEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes( "utf-16" ), "utf-16", -1 ).getBytes( "utf-8" ), + is( probe.getBytes( "utf-8" ) ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringNullByteArrayNullEncodingZeroBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringEmptyByteArrayNullEncodingZeroBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), null, 0 ), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringByteArrayNullEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes(), null, 0 ).getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringNullByteArrayJunkEncodingZeroBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), "junk", 0 ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringEmptyByteArrayJunkEncodingZeroBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyByteArray(), "junk", 0 ), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringByteArrayJunkEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( probe.getBytes(), "junk", 0 ).getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringNullByteArrayValidEncodingZeroBufSz() + throws Exception + { + IOUtil.toString( nullByteArray(), "utf-16", 0 ); + } + + /* + * copy(byte[],OutputStream) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullOutputStream() + throws Exception + { + IOUtil.copy( nullByteArray(), nullOutputStream() ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidOutputStream() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseByteArrayOutputStream() ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayNullOutputStream() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullOutputStream() ); + } + + @Test + public void copyEmptyByteArrayValidOutputStream() + throws Exception + { + IOUtil.copy( emptyByteArray(), new DontCloseByteArrayOutputStream() ); + } + + @Test + public void copyByteArrayValidOutputStream() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + byte[] input = { 1, 2, 3, 4, 5, 6 }; + IOUtil.copy( input, outputStream ); + assertThat( outputStream.toByteArray(), is( input ) ); + } + + /* + * copy(byte[],OutputStream,int) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullOutputStream()); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseByteArrayOutputStream()); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayNullOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullOutputStream()); + } + + @Test + public void copyEmptyByteArrayValidOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), new DontCloseByteArrayOutputStream()); + } + + @Test + public void copyByteArrayValidOutputStreamNegBufSz() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + byte[] input = { 1, 2, 3, 4, 5, 6 }; + IOUtil.copy( input, outputStream); + assertThat( outputStream.toByteArray(), is( input ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayNullOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullOutputStream()); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayValidOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseByteArrayOutputStream()); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayNullOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullOutputStream()); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayValidOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), new DontCloseByteArrayOutputStream()); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyByteArrayValidOutputStreamZeroBufSz() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + byte[] input = { 1, 2, 3, 4, 5, 6 }; + IOUtil.copy( input, outputStream); + assertThat( outputStream.toByteArray(), is( input ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayNullOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullOutputStream()); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayValidOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseByteArrayOutputStream()); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayNullOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullOutputStream()); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayValidOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), new DontCloseByteArrayOutputStream()); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyByteArrayValidOutputStreamPosBufSz() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + byte[] input = { 1, 2, 3, 4, 5, 6 }; + IOUtil.copy( input, outputStream); + assertThat( outputStream.toByteArray(), is( input ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullOutputStream() + throws Exception + { + IOUtil.copy( nullInputStream(), nullOutputStream() ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamValidOutputStream() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseByteArrayOutputStream() ); + } + + @Test + public void copyEmptyInputStreamNullOutputStream() + throws Exception + { + IOUtil.copy( new DontCloseByteArrayInputStream( emptyByteArray() ), nullOutputStream() ); + } + + @Test + public void copyEmptyInputStreamValidOutputStream() + throws Exception + { + IOUtil.copy( new DontCloseByteArrayInputStream( emptyByteArray() ), new DontCloseByteArrayOutputStream() ); + } + + @Test + public void copyInputStreamValidOutputStream() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + byte[] input = { 1, 2, 3, 4, 5, 6 }; + IOUtil.copy( new DontCloseByteArrayInputStream( input ), outputStream ); + assertThat( outputStream.toByteArray(), is( input ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyNullInputStreamNullOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullOutputStream(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyNullInputStreamValidOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseByteArrayOutputStream(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyInputStreamNullOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( new DontCloseByteArrayInputStream( emptyByteArray() ), nullOutputStream(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyInputStreamValidOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( new DontCloseByteArrayInputStream( emptyByteArray() ), new DontCloseByteArrayOutputStream(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyInputStreamValidOutputStreamNegBufSz() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + byte[] input = { 1, 2, 3, 4, 5, 6 }; + IOUtil.copy( new DontCloseByteArrayInputStream( input ), outputStream, -1 ); + assertThat( outputStream.toByteArray(), is( input ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamNullOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullOutputStream(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamValidOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new ByteArrayOutputStream(), 0 ); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamNullOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( new DontCloseByteArrayInputStream( emptyByteArray() ), nullOutputStream(), 0 ); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamValidOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( new DontCloseByteArrayInputStream( emptyByteArray() ), new DontCloseByteArrayOutputStream(), 0 ); + } + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamNullOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullOutputStream(), 1 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamValidOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new ByteArrayOutputStream(), 1 ); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamNullOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( new DontCloseByteArrayInputStream( emptyByteArray() ), nullOutputStream(), 1 ); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamValidOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( new DontCloseByteArrayInputStream( emptyByteArray() ), new DontCloseByteArrayOutputStream(), 1 ); + } + + @Test( timeout = INFINITE_LOOP_TIMEOUT ) + public void copyInputStreamValidOutputStreamPosBufSz() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + byte[] input = { 1, 2, 3, 4, 5, 6 }; + IOUtil.copy( new DontCloseByteArrayInputStream( input ), outputStream, 1 ); + assertThat( outputStream.toByteArray(), is( input ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStream() + throws Exception + { + IOUtil.toString( nullInputStream() ); + } + + @Test + public void toStringEmptyInputStream() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream() ), is( emptyString() ) ); + } + + @Test + public void toStringInputStream() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ) ).getBytes(), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStreamNegBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringEmptyInputStreamNegBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), -1 ), is( emptyString() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringInputStreamNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ), -1 ), is( probe ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringNullInputStreamZeroBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), 0 ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStreamPosBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), 1 ); + } + + @Test + public void toStringEmptyInputStreamPosBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), 1 ), is( emptyString() ) ); + } + + @Test + public void toStringInputStreamPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ), 1 ).getBytes(), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStreamNullEncoding() + throws Exception + { + IOUtil.toString( nullInputStream(), null ); + } + + @Test( expected = NullPointerException.class ) + public void toStringEmptyInputStreamNullEncoding() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), null ), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringInputStreamNullEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ), null ).getBytes(), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStreamJunkEncoding() + throws Exception + { + IOUtil.toString( nullInputStream(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void toStringEmptyInputStreamJunkEncoding() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), "junk" ), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void toStringInputStreamJunkEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ), "junk" ).getBytes(), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStreamValidEncoding() + throws Exception + { + IOUtil.toString( nullInputStream(), "utf-16" ); + } + + @Test + public void toStringEmptyInputStreamValidEncoding() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), "utf-16" ), is( emptyString() ) ); + } + + @Test + public void toStringInputStreamValidEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( + IOUtil.toString( new ByteArrayInputStream( probe.getBytes( "utf-16" ) ), "utf-16" ).getBytes( "utf-8" ), + is( probe.getBytes( "utf-8" ) ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStreamNullEncodingNegBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void toStringEmptyInputStreamNullEncodingNegBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), null, -1 ), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringInputStreamNullEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ), null, -1 ).getBytes(), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStreamJunkEncodingNegBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), "junk", -1 ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void toStringEmptyInputStreamJunkEncodingNegBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), "junk", -1 ), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void toStringInputStreamJunkEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ), "junk", -1 ).getBytes(), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullInputStreamValidEncodingNegBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), "utf-16", -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringEmptyInputStreamValidEncodingNegBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), "utf-16", -1 ), is( emptyString() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringInputStreamValidEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( + IOUtil.toString( new ByteArrayInputStream( probe.getBytes( "utf-16" ) ), "utf-16", -1 ).getBytes( "utf-8" ), + is( probe.getBytes( "utf-8" ) ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringNullInputStreamNullEncodingZeroBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringEmptyInputStreamNullEncodingZeroBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), null, 0 ), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringInputStreamNullEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ), null, 0 ).getBytes(), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringNullInputStreamJunkEncodingZeroBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), "junk", 0 ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringEmptyInputStreamJunkEncodingZeroBufSz() + throws Exception + { + assertThat( IOUtil.toString( emptyInputStream(), "junk", 0 ), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringInputStreamJunkEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new ByteArrayInputStream( probe.getBytes() ), "junk", 0 ).getBytes(), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void toStringNullInputStreamValidEncodingZeroBufSz() + throws Exception + { + IOUtil.toString( nullInputStream(), "utf-16", 0 ); + } + + /* + * copy(InputStream,Writer) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriter() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter() ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyInputStreamNullWriter() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter() ); + } + + @Test + public void copyEmptyInputStreamValidWriter() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyInputStream(), writer ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyInputStreamNullWriter() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), nullWriter() ); + } + + @Test + public void copyInputStreamValidWriter() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + /* + * copy(InputStream,Writer,int) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriterNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyInputStreamNullWriterNegBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyInputStreamValidWriterNegBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), new DontCloseStringWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyInputStreamNullWriterNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyInputStreamValidWriterNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer, -1 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamNullWriterZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamValidWriterZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamNullWriterZeroBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), 0 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriterPosBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamValidWriterPosBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyInputStreamNullWriterPosBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), 1 ); + } + + @Test + public void copyEmptyInputStreamValidWriterPosBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyInputStream(), writer, 1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test + public void copyInputStreamValidWriterPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer, 1 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + /* + * copy(InputStream,Writer,String) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriterNullEncoding() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), null ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamValidWriterNullEncoding() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), null ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyInputStreamNullWriterNullEncoding() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), null ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyInputStreamValidWriterNullEncoding() + throws Exception + { + IOUtil.copy( emptyInputStream(), new DontCloseStringWriter(), null ); + } + + @Test( expected = NullPointerException.class ) + public void copyInputStreamNullEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer, null ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriterJunkEncoding() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), "junk" ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamValidWriterJunkEncoding() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyEmptyInputStreamNullWriterJunkEncoding() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyEmptyInputStreamValidWriterJunkEncoding() + throws Exception + { + IOUtil.copy( emptyInputStream(), new DontCloseStringWriter(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyInputStreamNullWriterJunkEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), nullWriter(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyInputStreamValidWriterJunkEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer, "junk" ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriterValidEncoding() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), "utf-16" ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyInputStreamNullWriterValidEncoding() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), "utf-16" ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamValidWriterValidEncoding() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), "utf-16" ); + } + + @Test + public void copyEmptyInputStreamValidWriterValidEncoding() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyInputStream(), writer, "utf-16" ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyInputStreamNullWriterValidEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes( "utf-16" ) ), nullWriter(), "utf-16" ); + } + + @Test + public void copyInputStreamValidWriterValidEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes( "utf-16" ) ), writer, "utf-16" ); + assertThat( writer.toString().getBytes( "utf-8" ), is( probe.getBytes( "utf-8" ) ) ); + } + + /* + * copy(InputStream,Writer,String,int) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriterNullEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamValidWriterNullEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyInputStreamNullWriterNullEncodingNegBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyInputStreamValidWriterNullEncodingNegBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyInputStream(), writer, null, -1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyInputStreamNullWriterNullEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), nullWriter(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyInputStreamValidWriterNullEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer, null, -1 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriterJunkEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), "junk", -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamValidWriterJunkEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), "junk", -1 ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyEmptyInputStreamNullWriterJunkEncodingNegBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), "junk", -1 ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyEmptyInputStreamJunkEncodingNegBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyInputStream(), writer, "junk", -1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyInputStreamNullWriterJunkEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), nullWriter(), "junk", -1 ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyInputStreamValidWriterJunkEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer, "junk", -1 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamNullWriterValidEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), "utf-16", -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullInputStreamValidWriterValidEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), "utf-16", -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyInputStreamNullWriterValidEncodingNegBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), "utf-16", -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyInputStreamValidWriterValidEncodingNegBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyInputStream(), writer, "utf-16", -1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyInputStreamNullWriterValidEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes( "utf-16" ) ), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyInputStreamValidEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes( "utf-16" ) ), writer, "utf-16", -1 ); + assertThat( writer.toString().getBytes( "utf-8" ), is( probe.getBytes( "utf-8" ) ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamNullWriterNullEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamValidWriterNullEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamNullWriterNullEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamValidWriterNullEncodingZeroBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyInputStream(), writer, null, 0 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyInputStreamNullWriterNullEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), nullWriter(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyInputStreamValidWriterNullEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer, null, 0 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamNullWriterJunkEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), "junk", 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamValidWriterJunkEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), "junk", 0 ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamNullWriterJunkEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( emptyInputStream(), nullWriter(), "junk", 0 ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyInputStreamValidWriterJunkEncodingZeroBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyInputStream(), writer, "junk", 0 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyInputStreamNullWriterJunkEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), nullWriter(), "junk", 0 ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyInputStreamValidWriterJunkEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new ByteArrayInputStream( probe.getBytes() ), writer, "junk", 0 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamNullWriterValidEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), nullWriter(), "utf-16", 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullInputStreamValidWriterValidEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullInputStream(), new DontCloseStringWriter(), "utf-16", 0 ); + } + + /* + * copy(String,Writer) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullStringNullWriter() + throws Exception + { + IOUtil.copy( nullString(), nullWriter() ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyStringNullWriter() + throws Exception + { + IOUtil.copy( emptyString(), nullWriter() ); + } + + @Test + public void copyNullStringValidWriter() + throws Exception + { + IOUtil.copy( nullString(), new DontCloseStringWriter() ); + } + + @Test + public void copyEmptyStringValidWriter() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyString(), writer ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyStringNullWriter() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe, nullWriter() ); + } + + @Test + public void copyStringValidWriter() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe, writer ); + assertThat( writer.toString(), is( probe ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullStringNullOutputStream() + throws Exception + { + IOUtil.copy( nullString(), nullOutputStream() ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyStringNullOutputStream() + throws Exception + { + IOUtil.copy( emptyString(), nullOutputStream() ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullStringValidOutputStream() + throws Exception + { + IOUtil.copy( nullString(), new DontCloseByteArrayOutputStream() ); + } + + @Test + public void copyEmptyStringValidOutputStream() + throws Exception + { + ByteArrayOutputStream OutputStream = new DontCloseByteArrayOutputStream(); + IOUtil.copy( emptyString(), OutputStream ); + assertThat( OutputStream.toByteArray(), is( emptyString().getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyStringNullOutputStream() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe, nullOutputStream() ); + } + + @Test + public void copyStringValidOutputStream() + throws Exception + { + String probe = "A string \u2345\u00ef"; + ByteArrayOutputStream OutputStream = new DontCloseByteArrayOutputStream(); + IOUtil.copy( probe, OutputStream ); + assertThat( OutputStream.toByteArray(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullStringNullOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( nullString(), nullOutputStream(), -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyStringNullOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( emptyString(), nullOutputStream(), -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullStringValidOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( nullString(), new DontCloseByteArrayOutputStream(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyStringValidOutputStreamNegBufSz() + throws Exception + { + ByteArrayOutputStream OutputStream = new DontCloseByteArrayOutputStream(); + IOUtil.copy( emptyString(), OutputStream, -1 ); + assertThat( OutputStream.toByteArray(), is( emptyString().getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyStringNullOutputStreamNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe, nullOutputStream(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyStringValidOutputStreamNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + ByteArrayOutputStream OutputStream = new DontCloseByteArrayOutputStream(); + IOUtil.copy( probe, OutputStream, -1 ); + assertThat( OutputStream.toByteArray(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullStringNullOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( nullString(), nullOutputStream(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyStringNullOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( emptyString(), nullOutputStream(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullStringValidOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( nullString(), new DontCloseByteArrayOutputStream(), 0 ); + } + @Test( expected = NullPointerException.class ) + public void copyNullStringNullOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( nullString(), nullOutputStream(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyStringNullOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( emptyString(), nullOutputStream(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullStringValidOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( nullString(), new DontCloseByteArrayOutputStream(), 1 ); + } + + @Test + public void copyEmptyStringValidOutputStreamPosBufSz() + throws Exception + { + ByteArrayOutputStream OutputStream = new DontCloseByteArrayOutputStream(); + IOUtil.copy( emptyString(), OutputStream, 1 ); + assertThat( OutputStream.toByteArray(), is( emptyString().getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyStringNullOutputStreamPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe, nullOutputStream(), 1 ); + } + + @Test + public void copyStringValidOutputStreamPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + ByteArrayOutputStream OutputStream = new DontCloseByteArrayOutputStream(); + IOUtil.copy( probe, OutputStream, 1 ); + assertThat( OutputStream.toByteArray(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullReaderNullWriter() + throws Exception + { + IOUtil.copy( nullReader(), nullWriter() ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyReaderNullWriter() + throws Exception + { + IOUtil.copy( emptyReader(), nullWriter() ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullReaderValidWriter() + throws Exception + { + IOUtil.copy( nullReader(), new DontCloseStringWriter() ); + } + + @Test + public void copyEmptyReaderValidWriter() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyReader(), writer ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyReaderNullWriter() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new StringReader( probe ), nullWriter() ); + } + + @Test + public void copyReaderValidWriter() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new StringReader( probe ), writer ); + assertThat( writer.toString(), is( probe ) ); + } + + /* + * copy(Reader,Writer,int) + */ + + @Test( expected = NegativeArraySizeException.class ) + public void copyNullReaderNullWriterNegBufSz() + throws Exception + { + IOUtil.copy( nullReader(), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyReaderNullWriterNegBufSz() + throws Exception + { + IOUtil.copy( emptyReader(), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyNullReaderValidWriterNegBufSz() + throws Exception + { + IOUtil.copy( nullReader(), new DontCloseStringWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyReaderValidWriterNegBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyReader(), writer, -1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyReaderNullWriterNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new StringReader( probe ), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyReaderValidWriterNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new StringReader( probe ), writer, -1 ); + assertThat( writer.toString(), is( probe ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullReaderNullWriterZeroBufSz() + throws Exception + { + IOUtil.copy( nullReader(), nullWriter(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyReaderNullWriterZeroBufSz() + throws Exception + { + IOUtil.copy( emptyReader(), nullWriter(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullReaderValidWriterZeroBufSz() + throws Exception + { + IOUtil.copy( nullReader(), new DontCloseStringWriter(), 0 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullReaderNullWriterPosBufSz() + throws Exception + { + IOUtil.copy( nullReader(), nullWriter(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyReaderNullWriterPosBufSz() + throws Exception + { + IOUtil.copy( emptyReader(), nullWriter(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullReaderValidWriterPosBufSz() + throws Exception + { + IOUtil.copy( nullReader(), new DontCloseStringWriter(), 1 ); + } + + @Test + public void copyEmptyReaderValidWriterPosBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyReader(), writer, 1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyReaderNullWriterPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new StringReader( probe ), nullWriter(), 1 ); + } + + @Test + public void copyReaderValidWriterPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( new StringReader( probe ), writer, 1 ); + assertThat( writer.toString(), is( probe ) ); + } + + /* + * toByteArray(InputStream,int) + */ + + @Test( expected = NegativeArraySizeException.class ) + public void toByteArrayFromInputStreamNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( new DontCloseByteArrayInputStream( IOUtil.toByteArray( probe ) ), -1 ), + is( probe.getBytes() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toByteArrayNullInputStreamNegBufSz() + throws Exception + { + IOUtil.toByteArray( nullInputStream(), -1 ); + } + + @Test + public void toByteArrayFromInputStreamPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( new DontCloseByteArrayInputStream( IOUtil.toByteArray( probe ) ), +1 ), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toByteArrayNullInputStreamPosBufSz() + throws Exception + { + IOUtil.toByteArray( nullInputStream(), +1 ); + } + + /* + * toByteArray(Reader,int) + */ + + @Test( expected = NegativeArraySizeException.class ) + public void toByteArrayFromReaderNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( new DontCloseStringReader( probe ), -1 ), + is( probe.getBytes() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toByteArrayNullReaderNegBufSz() + throws Exception + { + IOUtil.toByteArray( nullReader(), -1 ); + } + + @Test + public void toByteArrayFromReaderPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( new DontCloseStringReader( probe ), +1 ), + is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toByteArrayNullReaderPosBufSz() + throws Exception + { + IOUtil.toByteArray( nullReader(), +1 ); + } + + /* + * toByteArray(String,int) + */ + + @Test( expected = NegativeArraySizeException.class ) + public void toByteArrayFromStringNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( probe, -1 ), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toByteArrayNullStringNegBufSz() + throws Exception + { + IOUtil.toByteArray( nullString(), -1 ); + } + + @Test + public void toByteArrayFromStringPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toByteArray( probe, +1 ), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void toByteArrayNullStringPosBufSz() + throws Exception + { + IOUtil.toByteArray( nullString(), +1 ); + } + + /* + * toString(Reader,int) + */ + + @Test( expected = NegativeArraySizeException.class ) + public void toStringFromReaderNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new DontCloseStringReader( probe ), -1 ), + is( probe) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void toStringNullReaderNegBufSz() + throws Exception + { + IOUtil.toString( nullReader(), -1 ); + } + + @Test + public void toStringFromReaderPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + assertThat( IOUtil.toString( new DontCloseStringReader( probe ), +1 ), + is( probe) ); + } + + @Test( expected = NullPointerException.class ) + public void toStringNullReaderPosBufSz() + throws Exception + { + IOUtil.toString( nullReader(), +1 ); + } + + /* + * copy(Reader,OutputStream) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullReaderNullOutputStream() + throws Exception + { + IOUtil.copy( nullReader(), nullOutputStream() ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullReaderValidOutputStream() + throws Exception + { + IOUtil.copy( nullReader(), new DontCloseByteArrayOutputStream() ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyReaderNullOutputStream() + throws Exception + { + IOUtil.copy( emptyReader(), nullOutputStream() ); + } + + @Test + public void copyEmptyReaderValidOutputStream() + throws Exception + { + IOUtil.copy( emptyReader(), new DontCloseByteArrayOutputStream() ); + } + + @Test + public void copyReaderValidOutputStream() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new DontCloseStringReader( probe ), outputStream ); + assertThat( outputStream.toByteArray(), is( probe.getBytes()) ); + } + + /* + * copy(Reader,OutputStream,int) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullReaderNullOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( nullReader(), nullOutputStream(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyNullReaderValidOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( nullReader(), new DontCloseByteArrayOutputStream(), -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyReaderNullOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( emptyReader(), nullOutputStream(), -1 ); + } + + @Test(expected = NegativeArraySizeException.class) + public void copyEmptyReaderValidOutputStreamNegBufSz() + throws Exception + { + IOUtil.copy( emptyReader(), new DontCloseByteArrayOutputStream(), -1 ); + } + + @Test(expected = NegativeArraySizeException.class) + public void copyReaderValidOutputStreamNegBufSz() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new DontCloseStringReader( probe ), outputStream, -1 ); + assertThat( outputStream.toByteArray(), is( probe.getBytes()) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullReaderNullOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( nullReader(), nullOutputStream(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullReaderValidOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( nullReader(), new DontCloseByteArrayOutputStream(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyReaderNullOutputStreamZeroBufSz() + throws Exception + { + IOUtil.copy( emptyReader(), nullOutputStream(), 0 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullReaderNullOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( nullReader(), nullOutputStream(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullReaderValidOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( nullReader(), new DontCloseByteArrayOutputStream(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyReaderNullOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( emptyReader(), nullOutputStream(), 1 ); + } + + @Test + public void copyEmptyReaderValidOutputStreamPosBufSz() + throws Exception + { + IOUtil.copy( emptyReader(), new DontCloseByteArrayOutputStream(), 1 ); + } + + @Test + public void copyReaderValidOutputStreamPosBufSz() + throws Exception + { + ByteArrayOutputStream outputStream = new DontCloseByteArrayOutputStream(); + String probe = "A string \u2345\u00ef"; + IOUtil.copy( new DontCloseStringReader( probe ), outputStream, 1 ); + assertThat( outputStream.toByteArray(), is( probe.getBytes()) ); + } + + /* + * copy(byte[],Writer) + */ + + /* + * copy(byte[],Writer,int) + */ + + /* + * copy(byte[],Writer,String) + */ + + /* + * copy(byte[],Writer,String,int) + */ + /* + * copy(byte[],Writer) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriter() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter() ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayNullWriter() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter() ); + } + + @Test + public void copyEmptyByteArrayValidWriter() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyByteArray(), writer ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyByteArrayNullWriter() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes(), nullWriter() ); + } + + @Test + public void copyByteArrayValidWriter() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + /* + * copy(byte[],Writer,int) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriterNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyByteArrayNullWriterNegBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyByteArrayValidWriterNegBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), new DontCloseStringWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyByteArrayNullWriterNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes(), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyByteArrayValidWriterNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer, -1 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayNullWriterZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayValidWriterZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayNullWriterZeroBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), 0 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriterPosBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidWriterPosBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), 1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayNullWriterPosBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), 1 ); + } + + @Test + public void copyEmptyByteArrayValidWriterPosBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyByteArray(), writer, 1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test + public void copyByteArrayValidWriterPosBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer, 1 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + /* + * copy(byte[],Writer,String) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriterNullEncoding() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), null ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidWriterNullEncoding() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), null ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayNullWriterNullEncoding() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), null ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayValidWriterNullEncoding() + throws Exception + { + IOUtil.copy( emptyByteArray(), new DontCloseStringWriter(), null ); + } + + @Test( expected = NullPointerException.class ) + public void copyByteArrayNullEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer, null ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriterJunkEncoding() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), "junk" ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidWriterJunkEncoding() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyEmptyByteArrayNullWriterJunkEncoding() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyEmptyByteArrayValidWriterJunkEncoding() + throws Exception + { + IOUtil.copy( emptyByteArray(), new DontCloseStringWriter(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyByteArrayNullWriterJunkEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes(), nullWriter(), "junk" ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyByteArrayValidWriterJunkEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer, "junk" ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriterValidEncoding() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), "utf-16" ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayNullWriterValidEncoding() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), "utf-16" ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidWriterValidEncoding() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), "utf-16" ); + } + + @Test + public void copyEmptyByteArrayValidWriterValidEncoding() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyByteArray(), writer, "utf-16" ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyByteArrayNullWriterValidEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes( "utf-16" ), nullWriter(), "utf-16" ); + } + + @Test + public void copyByteArrayValidWriterValidEncoding() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes( "utf-16" ), writer, "utf-16" ); + assertThat( writer.toString().getBytes( "utf-8" ), is( probe.getBytes( "utf-8" ) ) ); + } + + /* + * copy(byte[],Writer,String,int) + */ + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriterNullEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidWriterNullEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayNullWriterNullEncodingNegBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyEmptyByteArrayValidWriterNullEncodingNegBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyByteArray(), writer, null, -1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyByteArrayNullWriterNullEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes(), nullWriter(), null, -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyByteArrayValidWriterNullEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer, null, -1 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriterJunkEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), "junk", -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidWriterJunkEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), "junk", -1 ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyEmptyByteArrayNullWriterJunkEncodingNegBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), "junk", -1 ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyEmptyByteArrayJunkEncodingNegBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyByteArray(), writer, "junk", -1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyByteArrayNullWriterJunkEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes(), nullWriter(), "junk", -1 ); + } + + @Test( expected = UnsupportedEncodingException.class ) + public void copyByteArrayValidWriterJunkEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer, "junk", -1 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayNullWriterValidEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), "utf-16", -1 ); + } + + @Test( expected = NullPointerException.class ) + public void copyNullByteArrayValidWriterValidEncodingNegBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), "utf-16", -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyByteArrayNullWriterValidEncodingNegBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), "utf-16", -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyEmptyByteArrayValidWriterValidEncodingNegBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyByteArray(), writer, "utf-16", -1 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyByteArrayNullWriterValidEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes( "utf-16" ), nullWriter(), -1 ); + } + + @Test( expected = NegativeArraySizeException.class ) + public void copyByteArrayValidEncodingNegBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes( "utf-16" ), writer, "utf-16", -1 ); + assertThat( writer.toString().getBytes( "utf-8" ), is( probe.getBytes( "utf-8" ) ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayNullWriterNullEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayValidWriterNullEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayNullWriterNullEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayValidWriterNullEncodingZeroBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyByteArray(), writer, null, 0 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyByteArrayNullWriterNullEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes(), nullWriter(), null, 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyByteArrayValidWriterNullEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer, null, 0 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayNullWriterJunkEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), "junk", 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayValidWriterJunkEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), "junk", 0 ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayNullWriterJunkEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( emptyByteArray(), nullWriter(), "junk", 0 ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyEmptyByteArrayValidWriterJunkEncodingZeroBufSz() + throws Exception + { + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( emptyByteArray(), writer, "junk", 0 ); + assertThat( writer.toString(), is( emptyString() ) ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyByteArrayNullWriterJunkEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + IOUtil.copy( probe.getBytes(), nullWriter(), "junk", 0 ); + } + + @Test( expected = UnsupportedEncodingException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyByteArrayValidWriterJunkEncodingZeroBufSz() + throws Exception + { + String probe = "A string \u2345\u00ef"; + StringWriter writer = new DontCloseStringWriter(); + IOUtil.copy( probe.getBytes(), writer, "junk", 0 ); + assertThat( writer.toString().getBytes(), is( probe.getBytes() ) ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayNullWriterValidEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), nullWriter(), "utf-16", 0 ); + } + + @Test( expected = NullPointerException.class, timeout = INFINITE_LOOP_TIMEOUT ) + public void copyNullByteArrayValidWriterValidEncodingZeroBufSz() + throws Exception + { + IOUtil.copy( nullByteArray(), new DontCloseStringWriter(), "utf-16", 0 ); + } + + /* + * Utility methods + */ + private static byte[] nullByteArray() + { + return null; + } + + private static String nullString() + { + return null; + } + + private static OutputStream nullOutputStream() + { + return null; + } + + private static InputStream nullInputStream() + { + return null; + } + + private static Writer nullWriter() + { + return null; + } + + private static Reader nullReader() + { + return null; + } + + private static ByteArrayInputStream emptyInputStream() + { + return new ByteArrayInputStream( emptyByteArray() ); + } + + private static Reader emptyReader() + { + return new StringReader( emptyString() ); + } + + private static String emptyString() + { + return ""; + } + + private static byte[] emptyByteArray() + { + return new byte[0]; + } + + private static class DontCloseStringWriter + extends StringWriter + { + @Override + public void close() + throws IOException + { + throw new UnsupportedOperationException( "should not be called" ); + } + } + + private static class DontCloseStringReader + extends StringReader + { + + public DontCloseStringReader( String s ) + { + super( s ); + } + + @Override + public void close() + { + throw new UnsupportedOperationException( "should not be called" ); + } + } + + private static class DontCloseByteArrayInputStream + extends ByteArrayInputStream + { + public DontCloseByteArrayInputStream( byte[] input ) + { + super( input ); + } + + @Override + public void close() + throws IOException + { + throw new UnsupportedOperationException( "should not be called" ); + } + } + + private static class DontCloseByteArrayOutputStream + extends ByteArrayOutputStream + { + @Override + public void close() + throws IOException + { + throw new UnsupportedOperationException( "should not be called" ); + } + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java new file mode 100644 index 000000000..6c7e11d33 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java @@ -0,0 +1,38 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * @author Kristian Rosenvold + */ +@SuppressWarnings( "deprecation" ) +public class MatchPatternTest +{ + @Test + public void matchPath() + { + MatchPattern mp = MatchPattern.fromString( "ABC*" ); + assertTrue( mp.matchPath( "ABCD", true ) ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java new file mode 100644 index 000000000..84b41f964 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java @@ -0,0 +1,40 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author Kristian Rosenvold + */ +@SuppressWarnings( "deprecation" ) +public class MatchPatternsTest +{ + @Test + public void matches() + { + MatchPatterns from = MatchPatterns.from( "ABC**", "CDE**" ); + assertTrue( from.matches( "ABCDE", true ) ); + assertTrue( from.matches( "CDEF", true ) ); + assertFalse( from.matches( "XYZ", true ) ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/SelectorUtilsTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/SelectorUtilsTest.java new file mode 100644 index 000000000..750015a74 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/SelectorUtilsTest.java @@ -0,0 +1,84 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import static org.junit.Assert.*; + +import java.io.File; + +import org.junit.Test; + +/** + * Test the {@link SelectorUtils} class. + */ +@SuppressWarnings( "deprecation" ) +public class SelectorUtilsTest +{ + + @Test( expected = NullPointerException.class ) + public void testMatchPatternStart() + { + SelectorUtils.matchPatternStart( null, null ); + } + + @Test + public void testEmptyStrings() + { + assertTrue( SelectorUtils.matchPatternStart( "", "" ) ); + } + + @Test + public void testRegexPrefix() + throws Exception + { + assertEquals( true, + SelectorUtils.matchPatternStart( SelectorUtils.REGEX_HANDLER_PREFIX + File.separator + "aaa" + + SelectorUtils.PATTERN_HANDLER_SUFFIX, "" ) ); + } + + @Test + public void testAntPatternStrings() + { + assertAntDoesNotMatch( "/aaa", "" ); + assertAntDoesNotMatch( "\\aaa", "" ); + assertAntMatch( "aaa", "" ); + assertAntMatch( "/aaa/bbb", "/aaa/bbb" ); + assertAntMatch( "/aaa/**", "/aaa/bbb" ); + assertAntDoesNotMatch( "/aaa/**", "/ccc/bbb" ); + assertAntMatch( "/aaa/**", "\\aaa\\bbb" ); + assertAntDoesNotMatch( "/aaa/**", "\\ccc\\bbb" ); + assertAntDoesNotMatch( "/aaa/", "\\aaa\\bbb" ); + } + + private void assertAntDoesNotMatch( String pattern, String target ) + { + assertEquals( false, SelectorUtils.matchPatternStart( wrapWithAntHandler( pattern ), target ) ); + } + + private void assertAntMatch( String pattern, String target ) + { + assertEquals( true, SelectorUtils.matchPatternStart( wrapWithAntHandler( pattern ), target ) ); + } + + private String wrapWithAntHandler( String val ) + { + return SelectorUtils.ANT_HANDLER_PREFIX + val + SelectorUtils.PATTERN_HANDLER_SUFFIX; + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/SymlinkTestSetup.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/SymlinkTestSetup.java new file mode 100644 index 000000000..9283c9703 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/io/SymlinkTestSetup.java @@ -0,0 +1,62 @@ +package org.apache.maven.shared.utils.io; + + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import static org.apache.commons.io.FileUtils.write; + +/* + * 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. + */ +public class SymlinkTestSetup +{ + /** + * Creates a standard directory layout with symlinks and files. + */ + public static File createStandardSymlinkTestDir( File root ) + throws IOException + { + File srcDir = new File( root, "/src" ); + srcDir.mkdirs(); + File target = new File( srcDir, "targetDir" ); + target.mkdirs(); + write( new File( target, "targetFile.txt" ), "a regular File payload", StandardCharsets.UTF_8 ); + File aRegularDir = new File( srcDir, "aRegularDir" ); + aRegularDir.mkdirs(); + write( new File( aRegularDir, "aRegularFile.txt" ), "a regular File payload", StandardCharsets.UTF_8 ); + + File dirOnTheOutside = new File( root, "dirOnTheOutside" ); + dirOnTheOutside.mkdirs(); + write( new File( dirOnTheOutside, "FileInDirOnTheOutside.txt" ), "a file in dir on the outside", StandardCharsets.UTF_8 ); + write( new File( root, "onTheOutside.txt" ), "A file on the outside", StandardCharsets.UTF_8 ); + write( new File( srcDir, "fileR.txt" ), "FileR payload", StandardCharsets.UTF_8 ); + write( new File( srcDir, "fileW.txt" ), "FileW payload", StandardCharsets.UTF_8 ); + write( new File( srcDir, "fileX.txt" ), "FileX payload", StandardCharsets.UTF_8 ); + // todo: set file attributes (not used here) + + FileUtils.createSymbolicLink( new File( srcDir, "symDir" ), new File( "targetDir" ) ); + FileUtils.createSymbolicLink( new File( srcDir, "symLinkToDirOnTheOutside" ), new File( "../dirOnTheOutside" ) ); + FileUtils.createSymbolicLink( new File( srcDir, "symLinkToFileOnTheOutside" ), new File( "../onTheOutside.txt" ) ); + FileUtils.createSymbolicLink( new File( srcDir, "symR" ), new File( "fileR.txt" ) ); + FileUtils.createSymbolicLink( new File( srcDir, "symW" ), new File( "fileW.txt" ) ); + FileUtils.createSymbolicLink( new File( srcDir, "symX" ), new File( "fileX.txt" ) ); + return srcDir; + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/logging/AnsiMessageBuilderTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/logging/AnsiMessageBuilderTest.java new file mode 100644 index 000000000..2c580e2a5 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/logging/AnsiMessageBuilderTest.java @@ -0,0 +1,111 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +public class AnsiMessageBuilderTest +{ + + private AnsiMessageBuilder ansiMessageBuilder; + + @Before + public void initializeAnsiMessageBuffer() + { + this.ansiMessageBuilder = new AnsiMessageBuilder(); + } + + @Test + public void should_color_debug() + { + ansiMessageBuilder.debug( "DEBUG" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[1;36mDEBUG\u001B[m" ) ); + } + + @Test + public void should_color_info() + { + ansiMessageBuilder.info( "INFO" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[1;34mINFO\u001B[m" ) ); + } + + @Test + public void should_color_warning_and_reset() + { + ansiMessageBuilder.warning( "WARNING" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[1;33mWARNING\u001B[m" ) ); + } + + @Test + public void should_color_error() + { + ansiMessageBuilder.error( "ERROR" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[1;31mERROR\u001B[m" ) ); + } + + @Test + public void should_color_success_with_message() + { + ansiMessageBuilder.success( "a success message" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[1;32ma success message\u001B[m" ) ); + } + + @Test + public void should_color_failure_and_reset() + { + ansiMessageBuilder.failure( "a failure message" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[1;31ma failure message\u001B[m" ) ); + } + + @Test + public void should_color_strong_and_reset() + { + ansiMessageBuilder.strong( "a strong message" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[1ma strong message\u001B[m" ) ); + } + + @Test + public void should_color_mojo_and_reset() + { + ansiMessageBuilder.mojo( "a mojo" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[32ma mojo\u001B[m" ) ); + } + + @Test + public void should_color_project_and_reset() + { + ansiMessageBuilder.project( "a project" ); + + assertThat( ansiMessageBuilder.toString(), equalTo( "\u001B[36ma project\u001B[m" ) ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/logging/MessageUtilsTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/logging/MessageUtilsTest.java new file mode 100644 index 000000000..a53c4d969 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/logging/MessageUtilsTest.java @@ -0,0 +1,48 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.PrintStream; + +import org.junit.Test; + +public class MessageUtilsTest +{ + @Test + public void testSystem() + { + PrintStream currentOut = System.out; + try + { + MessageUtils.systemInstall(); + assertThat( System.out, not( sameInstance( currentOut ) ) ); + MessageUtils.systemUninstall(); + assertThat( System.out, sameInstance( currentOut ) ); + } + finally + { + System.setOut( currentOut ); + } + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/testhelpers/FileTestHelper.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/testhelpers/FileTestHelper.java new file mode 100644 index 000000000..aa7323cd4 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/testhelpers/FileTestHelper.java @@ -0,0 +1,104 @@ +package org.apache.maven.shared.utils.testhelpers; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import org.apache.commons.io.FileUtils; + +import org.junit.rules.TemporaryFolder; + +/** + * A few utility methods for file based tests. + */ +public final class FileTestHelper +{ + + private FileTestHelper() + { + // utility function doesn't need a public constructor + } + + public static void generateTestData( OutputStream out, long size ) + throws IOException + { + for ( int i = 0; i < size; i++ ) + { + // nice varied byte pattern compatible with Readers and Writers + out.write( (byte) ( ( i % 127 ) + 1 ) ); + } + } + + public static void generateTestFile( File testfile, int size ) + throws IOException + { + if ( testfile.exists() ) + { + //noinspection ResultOfMethodCallIgnored + testfile.delete(); + } + + try ( OutputStream os = new FileOutputStream( testfile ) ) { + generateTestData( os, size ); + os.flush(); + } + } + + public static void createLineBasedFile( File file, String[] data ) + throws IOException + { + if ( file.getParentFile() != null && !file.getParentFile().exists() ) + { + throw new IOException( "Cannot create file " + file + " as the parent directory does not exist" ); + } + + try ( Writer out = new OutputStreamWriter( new FileOutputStream( file ), "UTF-8" ) ) + { + for ( String aData : data ) + { + out.write( aData ); + out.write( System.lineSeparator() ); + } + } + } + + /** + * Check if the given file exists in the given folder and remove it. + * + * @return the File object for a new file + * @throws IOException + */ + public static File newFile( TemporaryFolder folder, String filename ) + throws IOException + { + File destination = folder.newFile( filename ); + + if ( destination.exists() ) + { + FileUtils.deleteQuietly( destination ); + } + return destination; + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/PrettyPrintXmlWriterTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/PrettyPrintXmlWriterTest.java new file mode 100644 index 000000000..70f078c50 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/PrettyPrintXmlWriterTest.java @@ -0,0 +1,192 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; + +import javax.swing.text.html.HTML; +import java.io.StringWriter; + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test of {@link PrettyPrintXMLWriter} + * + * @author Vincent Siveton + * + */ +public class PrettyPrintXmlWriterTest +{ + private StringWriter w = new StringWriter(); + private PrettyPrintXMLWriter writer = new PrettyPrintXMLWriter( w ); + + @Test + public void testDefaultPrettyPrintXMLWriter() throws IOException + { + writer.startElement( HTML.Tag.HTML.toString() ); + + writeXhtmlHead( writer ); + + writeXhtmlBody( writer ); + + writer.endElement(); // Tag.HTML + + Assert.assertEquals( expectedResult( Os.LINE_SEP ), w.toString() ); + } + + @Test + public void testPrettyPrintXMLWriterWithGivenLineSeparator() throws IOException + { + writer.setLineSeparator( "\n" ); + + writer.startElement( HTML.Tag.HTML.toString() ); + + writeXhtmlHead( writer ); + + writeXhtmlBody( writer ); + + writer.endElement(); // Tag.HTML + + Assert.assertEquals( expectedResult( "\n" ), w.toString() ); + } + + @Test + public void testPrettyPrintXMLWriterWithGivenLineIndenter() throws IOException + { + writer.setLineIndenter( " " ); + + writer.startElement( HTML.Tag.HTML.toString() ); + + writeXhtmlHead( writer ); + + writeXhtmlBody( writer ); + + writer.endElement(); // Tag.HTML + + Assert.assertEquals( expectedResult( " ", Os.LINE_SEP ), w.toString() ); + } + + @Test + public void testEscapeXmlAttributeWindows() throws IOException + { + // Windows + writer.startElement( HTML.Tag.DIV.toString() ); + writer.addAttribute( "class", "sect\r\nion" ); + writer.endElement(); // Tag.DIV + Assert.assertEquals( "
          ", w.toString() ); + } + + @Test + public void testEscapeXmlAttributeMac() throws IOException + { + // Mac + writer.startElement( HTML.Tag.DIV.toString() ); + writer.addAttribute( "class", "sect\rion" ); + writer.endElement(); // Tag.DIV + Assert.assertEquals( "
          ", w.toString() ); + } + + @Test + public void testEscapeXmlAttributeTrailingCR() throws IOException + { + // Mac + writer.startElement( HTML.Tag.DIV.toString() ); + writer.addAttribute( "class", "section\r" ); + writer.endElement(); // Tag.DIV + Assert.assertEquals( "
          ", w.toString() ); + } + + @Test + public void testEscapeXmlAttributeUnix() throws IOException + { + // Unix + writer.startElement( HTML.Tag.DIV.toString() ); + writer.addAttribute( "class", "sect\nion" ); + writer.endElement(); // Tag.DIV + Assert.assertEquals( "
          ", w.toString() ); + } + + private void writeXhtmlHead( XMLWriter writer ) throws IOException + { + writer.startElement( HTML.Tag.HEAD.toString() ); + writer.startElement( HTML.Tag.TITLE.toString() ); + writer.writeText( "title" ); + writer.endElement(); // Tag.TITLE + writer.startElement( HTML.Tag.META.toString() ); + writer.addAttribute( "name", "author" ); + writer.addAttribute( "content", "Author" ); + writer.endElement(); // Tag.META + writer.startElement( HTML.Tag.META.toString() ); + writer.addAttribute( "name", "date" ); + writer.addAttribute( "content", "Date" ); + writer.endElement(); // Tag.META + writer.endElement(); // Tag.HEAD + } + + private void writeXhtmlBody( XMLWriter writer ) throws IOException + { + writer.startElement( HTML.Tag.BODY.toString() ); + writer.startElement( HTML.Tag.P.toString() ); + writer.writeText( "Paragraph 1, line 1. Paragraph 1, line 2." ); + writer.endElement(); // Tag.P + writer.startElement( HTML.Tag.DIV.toString() ); + writer.addAttribute( "class", "section" ); + writer.startElement( HTML.Tag.H2.toString() ); + writer.writeText( "Section title" ); + writer.endElement(); // Tag.H2 + writer.endElement(); // Tag.DIV + writer.endElement(); // Tag.BODY + } + + private static String expectedResult( String lineSeparator ) + { + return expectedResult( " ", lineSeparator ); + } + + private static String expectedResult( String lineIndenter, String lineSeparator ) + { + StringBuilder expected = new StringBuilder(); + + expected.append( "" ).append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 1 ) ).append( "" ).append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 2 ) ).append( "title" ) + .append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 2 ) ) + .append( "" ).append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 2 ) ).append( "" ) + .append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 1 ) ).append( "" ).append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 1 ) ).append( "" ).append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 2 ) ) + .append( "

          Paragraph 1, line 1. Paragraph 1, line 2.

          " ).append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 2 ) ).append( "
          " ) + .append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 3 ) ).append( "

          Section title

          " ) + .append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 2 ) ).append( "
          " ).append( lineSeparator ); + expected.append( StringUtils.repeat( lineIndenter, 1 ) ).append( "" ).append( lineSeparator ); + expected.append( "" ); + + return expected.toString(); + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/XmlWriterUtilTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/XmlWriterUtilTest.java new file mode 100644 index 000000000..a22cece18 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/XmlWriterUtilTest.java @@ -0,0 +1,436 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.Writer; +import org.apache.maven.shared.utils.StringUtils; +import org.apache.maven.shared.utils.WriterFactory; + +import junit.framework.TestCase; + +/** + * @author Vincent Siveton + * + */ +public class XmlWriterUtilTest + extends TestCase +{ + private OutputStream output; + + private Writer writer; + + private XMLWriter xmlWriter; + + /** {@inheritDoc} */ + protected void setUp() + throws Exception + { + super.setUp(); + + output = new ByteArrayOutputStream(); + writer = WriterFactory.newXmlWriter( output ); + xmlWriter = new PrettyPrintXMLWriter( writer ); + } + + /** {@inheritDoc} */ + protected void tearDown() + throws Exception + { + super.tearDown(); + + xmlWriter = null; + writer = null; + output = null; + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeLineBreak(XMLWriter)}. + * + * @throws Exception if any + */ + public void testWriteLineBreakXMLWriter() + throws Exception + { + XmlWriterUtil.writeLineBreak( xmlWriter ); + writer.close(); + System.out.println( "output = " + output.toString() + "x"); + assertTrue( StringUtils.countMatches( output.toString(), XmlWriterUtil.LS ) == 1 ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeLineBreak(XMLWriter, int)}. + * + * @throws Exception if any + */ + public void testWriteLineBreakXMLWriterInt() + throws Exception + { + XmlWriterUtil.writeLineBreak( xmlWriter, 10 ); + writer.close(); + assertTrue( StringUtils.countMatches( output.toString(), XmlWriterUtil.LS ) == 10 ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeLineBreak(XMLWriter, int, int)}. + * + * @throws Exception if any + */ + public void testWriteLineBreakXMLWriterIntInt() + throws Exception + { + XmlWriterUtil.writeLineBreak( xmlWriter, 10, 2 ); + writer.close(); + assertTrue( StringUtils.countMatches( output.toString(), XmlWriterUtil.LS ) == 10 ); + assertTrue( StringUtils.countMatches( output.toString(), StringUtils + .repeat( " ", 2 * XmlWriterUtil.DEFAULT_INDENTATION_SIZE ) ) == 1 ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeLineBreak(XMLWriter, int, int, int)}. + * + * @throws Exception if any + */ + public void testWriteLineBreakXMLWriterIntIntInt() + throws Exception + { + XmlWriterUtil.writeLineBreak( xmlWriter, 10, 2, 4 ); + writer.close(); + assertTrue( StringUtils.countMatches( output.toString(), XmlWriterUtil.LS ) == 10 ); + assertTrue( StringUtils.countMatches( output.toString(), StringUtils.repeat( " ", 2 * 4 ) ) == 1 ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeCommentLineBreak(XMLWriter)}. + * + * @throws Exception if any + */ + public void testWriteCommentLineBreakXMLWriter() + throws Exception + { + XmlWriterUtil.writeCommentLineBreak( xmlWriter ); + writer.close(); + StringBuilder sb = new StringBuilder(); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == XmlWriterUtil.DEFAULT_COLUMN_LINE - 1 + XmlWriterUtil.LS.length() ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeCommentLineBreak(XMLWriter, int)}. + * + * @throws Exception if any + */ + public void testWriteCommentLineBreakXMLWriterInt() + throws Exception + { + XmlWriterUtil.writeCommentLineBreak( xmlWriter, 20 ); + writer.close(); + assertEquals( output.toString(), "" + XmlWriterUtil.LS ); + + tearDown(); + setUp(); + + XmlWriterUtil.writeCommentLineBreak( xmlWriter, 10 ); + writer.close(); + assertEquals( output.toString(), output.toString(), "" + XmlWriterUtil.LS ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeComment(XMLWriter, java.lang.String)}. + * + * @throws Exception if any + */ + public void testWriteCommentXMLWriterString() + throws Exception + { + XmlWriterUtil.writeComment( xmlWriter, "hello" ); + writer.close(); + StringBuffer sb = new StringBuffer(); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == XmlWriterUtil.DEFAULT_COLUMN_LINE - 1 + XmlWriterUtil.LS.length() ); + + tearDown(); + setUp(); + + XmlWriterUtil.writeComment( xmlWriter, + "hellooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" ); + writer.close(); + sb = new StringBuffer(); + sb.append( "" ) + .append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() >= XmlWriterUtil.DEFAULT_COLUMN_LINE ); + + tearDown(); + setUp(); + + XmlWriterUtil.writeComment( xmlWriter, "hello\nworld" ); + writer.close(); + sb = new StringBuffer(); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == 2 * ( XmlWriterUtil.DEFAULT_COLUMN_LINE - 1 + XmlWriterUtil.LS.length() ) ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeComment(XMLWriter, java.lang.String, int)}. + * + * @throws Exception if any + */ + public void testWriteCommentXMLWriterStringInt() + throws Exception + { + String indent = StringUtils.repeat( " ", 2 * XmlWriterUtil.DEFAULT_INDENTATION_SIZE ); + + XmlWriterUtil.writeComment( xmlWriter, "hello", 2 ); + writer.close(); + StringBuffer sb = new StringBuffer(); + sb.append( indent ); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == XmlWriterUtil.DEFAULT_COLUMN_LINE - 1 + XmlWriterUtil.LS.length() + 2 + * XmlWriterUtil.DEFAULT_INDENTATION_SIZE ); + + tearDown(); + setUp(); + + XmlWriterUtil.writeComment( xmlWriter, "hello\nworld", 2 ); + writer.close(); + sb = new StringBuffer(); + sb.append( indent ); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( indent ); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == 2 * ( XmlWriterUtil.DEFAULT_COLUMN_LINE - 1 + XmlWriterUtil.LS.length() ) + 2 * indent.length() ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeComment(XMLWriter, java.lang.String, int, int)}. + * + * @throws Exception if any + */ + public void testWriteCommentXMLWriterStringIntInt() + throws Exception + { + String repeat = StringUtils.repeat( " ", 2 * 4 ); + + XmlWriterUtil.writeComment( xmlWriter, "hello", 2, 4 ); + writer.close(); + StringBuffer sb = new StringBuffer(); + sb.append( repeat ); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == XmlWriterUtil.DEFAULT_COLUMN_LINE - 1 + XmlWriterUtil.LS.length() + 2 * 4 ); + + tearDown(); + setUp(); + + XmlWriterUtil.writeComment( xmlWriter, "hello\nworld", 2, 4 ); + writer.close(); + sb = new StringBuffer(); + sb.append( repeat ); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( repeat ); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == 2 * ( XmlWriterUtil.DEFAULT_COLUMN_LINE - 1 + XmlWriterUtil.LS.length() ) + 2 * repeat.length() ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeComment(XMLWriter, java.lang.String, int, int, int)}. + * + * @throws Exception if any + */ + public void testWriteCommentXMLWriterStringIntIntInt() + throws Exception + { + String indent = StringUtils.repeat( " ", 2 * 4 ); + + XmlWriterUtil.writeComment( xmlWriter, "hello", 2, 4, 50 ); + writer.close(); + StringBuffer sb = new StringBuffer(); + sb.append( indent ); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == 50 - 1 + XmlWriterUtil.LS.length() + 2 * 4 ); + + tearDown(); + setUp(); + + XmlWriterUtil.writeComment( xmlWriter, "hello", 2, 4, 10 ); + writer.close(); + sb = new StringBuffer(); + sb.append( indent ); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() >= 10 + 2 * 4 ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeCommentText(XMLWriter, java.lang.String, int)}. + * + * @throws Exception if any + */ + public void testWriteCommentTextXMLWriterStringInt() + throws Exception + { + XmlWriterUtil.writeCommentText( xmlWriter, "hello", 0 ); + writer.close(); + StringBuffer sb = new StringBuffer(); + sb.append( XmlWriterUtil.LS ); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == 3 * ( 80 - 1 + XmlWriterUtil.LS.length() ) + 2 * XmlWriterUtil.LS.length() ); + + tearDown(); + setUp(); + + String indent = StringUtils.repeat( " ", 2 * 2 ); + + XmlWriterUtil.writeCommentText( xmlWriter, "hello world with end of line\n and " + + "loooooooooooooooooooooooooooooooooooooooooooooooooooooonnnnnnnnnnong line", 2 ); + writer.close(); + sb = new StringBuffer(); + sb.append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( XmlWriterUtil.LS ); + sb.append( indent ); + assertEquals( output.toString(), sb.toString() ); + } + + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeCommentText(XMLWriter, java.lang.String, int, int)}. + * + * @throws Exception if any + */ + public void testWriteCommentTextXMLWriterStringIntInt() + throws Exception + { + String indent = StringUtils.repeat( " ", 2 * 4 ); + + XmlWriterUtil.writeCommentText( xmlWriter, "hello", 2, 4 ); + writer.close(); + StringBuilder sb = new StringBuilder(); + sb.append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ) + .append( XmlWriterUtil.LS ); + sb.append( XmlWriterUtil.LS ); + sb.append( indent ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == 3 * ( 80 - 1 + XmlWriterUtil.LS.length() ) + 4 * 2 * 4 + 2 * XmlWriterUtil.LS.length() ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeCommentText(XMLWriter, java.lang.String, int, int, int)}. + * + * @throws Exception if any + */ + public void testWriteCommentTextXMLWriterStringIntIntInt() + throws Exception + { + String indent = StringUtils.repeat( " ", 2 * 4 ); + + XmlWriterUtil.writeCommentText( xmlWriter, "hello", 2, 4, 50 ); + writer.close(); + StringBuilder sb = new StringBuilder(); + sb.append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ).append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ).append( XmlWriterUtil.LS ); + sb.append( indent ).append( "" ).append( XmlWriterUtil.LS ); + sb.append( XmlWriterUtil.LS ); + sb.append( indent ); + assertEquals( output.toString(), sb.toString() ); + assertTrue( output.toString().length() == 3 * ( 50 - 1 + XmlWriterUtil.LS.length() ) + 4 * 2 * 4 + 2 * XmlWriterUtil.LS.length() ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeComment(XMLWriter, java.lang.String)}. + * + * @throws Exception if any + */ + public void testWriteCommentNull() + throws Exception + { + XmlWriterUtil.writeComment( xmlWriter, null ); + writer.close(); + StringBuilder sb = new StringBuilder(); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeComment(XMLWriter, java.lang.String)}. + * + * @throws Exception if any + */ + public void testWriteCommentShort() + throws Exception + { + XmlWriterUtil.writeComment( xmlWriter, "This is a short text" ); + writer.close(); + StringBuilder sb = new StringBuilder(); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + } + + /** + * Test method for {@link org.apache.maven.shared.utils.xml.XmlWriterUtil#writeComment(XMLWriter, java.lang.String)}. + * + * @throws Exception if any + */ + public void testWriteCommentLong() + throws Exception + { + XmlWriterUtil.writeComment( xmlWriter, "Maven is a software project management and comprehension tool. " + + "Based on the concept of a project object model (POM), Maven can manage a project's build, reporting " + + "and documentation from a central piece of information." ); + writer.close(); + StringBuilder sb = new StringBuilder(); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( "" ).append( XmlWriterUtil.LS ); + sb.append( "" ).append( XmlWriterUtil.LS ); + assertEquals( output.toString(), sb.toString() ); + } +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/Xpp3DomBuilderTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/Xpp3DomBuilderTest.java new file mode 100644 index 000000000..9aaca9f15 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/Xpp3DomBuilderTest.java @@ -0,0 +1,185 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.StringUtils; +import org.apache.maven.shared.utils.xml.pull.XmlPullParserException; + +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + + +/** + * @author Kristian Rosenvold + */ +public class Xpp3DomBuilderTest +{ + + private static final String LS = System.getProperty( "line.separator" ); + + private static final String xmlDeclaration = "\n"; + + @Test + public void selfClosingTag() + throws Exception + { + + // Todo: http://stackoverflow.com/questions/12968390/detecting-self-closing-tags-in-sax + String domString = selfClosingTagSource(); + + Xpp3Dom dom = Xpp3DomBuilder.build( new StringReader( domString ) ); + + String expected = expectedSelfClosingTag(); + String dom1Str = dom.toString(); + assertEquals( "check DOMs match", expected, dom1Str ); + } + + @Test + public void trimming() + throws Exception + { + String domString = createDomString(); + + Xpp3Dom dom = Xpp3DomBuilder.build( new StringReader( domString ), true ); + + assertEquals( "element1value", dom.getChild( "element1" ).getValue() ); + + assertEquals( " preserve space ", dom.getChild( "element6" ).getValue() ); + + dom = Xpp3DomBuilder.build( new StringReader( domString ), false ); + + assertEquals( " element1value\n ", dom.getChild( "element1" ).getValue() ); + + assertEquals( " preserve space ", dom.getChild( "element6" ).getValue() ); + } + + @Test(expected = XmlPullParserException.class) + public void malformedXml() + { + Xpp3DomBuilder.build( new StringReader( "" + createDomString() ) ); + fail( "We're supposed to fail" ); + } + + @Test + public void attributeEscaping() + throws IOException, XmlPullParserException + { + String s = getAttributeEncodedString(); + Xpp3Dom dom = Xpp3DomBuilder.build( new StringReader( s ) ); + + assertEquals( "", dom.getChild( "el" ).getAttribute( "att" ) ); + StringWriter w = new StringWriter(); + Xpp3DomWriter.write( w, dom ); + String newString = w.toString(); + assertEquals( newString, s ); + } + + @Test + public void contentEscaping() + throws IOException, XmlPullParserException + { + Xpp3Dom dom = Xpp3DomBuilder.build( new StringReader( getEncodedString() ) ); + + assertEquals( "\"msg\"", dom.getChild( "a1" ).getValue() ); + assertEquals( "\"msg\"", dom.getChild( "a2" ).getValue() ); + assertEquals( "\"msg\"", dom.getChild( "a3" ).getValue() ); + + StringWriter w = new StringWriter(); + Xpp3DomWriter.write( w, dom ); + assertEquals( getExpectedString(), w.toString() ); + } + + private static String getAttributeEncodedString() + { + StringBuilder domString = new StringBuilder(); + domString.append( "" ); + domString.append( LS ); + domString.append( " bar" ); + domString.append( LS ); + domString.append( "" ); + + return domString.toString(); + } + + private static String getEncodedString() + { + StringBuilder domString = new StringBuilder(); + domString.append( "\n" ); + domString.append( " \"msg\"\n" ); + domString.append( " \"msg\"]]>\n" ); + domString.append( " <b>"msg"</b>\n" ); + domString.append( "" ); + + return domString.toString(); + } + + private static String getExpectedString() + { + StringBuilder domString = new StringBuilder(); + domString.append( "" ); + domString.append( LS ); + domString.append( " \"msg\"" ); + domString.append( LS ); + domString.append( " <b>\"msg\"</b>" ); + domString.append( LS ); + domString.append( " <b>\"msg\"</b>" ); + domString.append( LS ); + domString.append( "" ); + return domString.toString(); + } + + private static String createDomString() + { + StringBuilder buf = new StringBuilder(); + buf.append( "\n" ); + buf.append( " element1value\n \n" ); + buf.append( " \n" ); + buf.append( " element3value\n" ); + buf.append( " \n" ); + buf.append( " \n" ); + buf.append( " \n" ); + buf.append( " preserve space \n" ); + buf.append( "\n" ); + + return buf.toString(); + } + + private static String selfClosingTagSource() + { + StringBuilder buf = new StringBuilder(); + buf.append( "\n" ); + buf.append( " \n" ); + buf.append( " \n" ); + buf.append( "" ); + return StringUtils.unifyLineSeparators( buf.toString() ); + } + + private static String expectedSelfClosingTag() + { + return StringUtils.unifyLineSeparators( xmlDeclaration + selfClosingTagSource() ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/pull/Xpp3DomTest.java b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/pull/Xpp3DomTest.java new file mode 100644 index 000000000..3d594bf8a --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/java/org/apache/maven/shared/utils/xml/pull/Xpp3DomTest.java @@ -0,0 +1,273 @@ +package org.apache.maven.shared.utils.xml.pull; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.StringReader; +import org.apache.maven.shared.utils.xml.Xpp3Dom; +import org.apache.maven.shared.utils.xml.Xpp3DomBuilder; + +import org.junit.Test; + +import static org.apache.maven.shared.utils.xml.Xpp3Dom.mergeXpp3Dom; +import static org.junit.Assert.*; + +/** + * @author Kristian Rosenvold + */ +public class Xpp3DomTest +{ + + private Xpp3Dom createElement( String element, String value ) + { + Xpp3Dom t1s1 = new Xpp3Dom( element ); + t1s1.setValue( value ); + return t1s1; + } + + + @Test + public void mergePrecedenceSelfClosed() + throws XmlPullParserException, IOException + { + Xpp3Dom parentConfig = build( "" ); + Xpp3Dom childConfig = build( "ooopise" ); + + Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( childConfig, parentConfig ); + Xpp3Dom items = result.getChild( "items" ); + + assertEquals( 1, items.getChildCount() ); + Xpp3Dom item = items.getChild( 0 ); + assertEquals( "ooopise", item.getValue() ); + } + + @Test + public void mergePrecedenceOpenClose() + throws XmlPullParserException, IOException + { + Xpp3Dom parentConfig = build( "" ); + Xpp3Dom childConfig = build( "ooopise" ); + + Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( childConfig, parentConfig ); + Xpp3Dom items = result.getChild( "items" ); + + assertEquals( 1, items.getChildCount() ); + Xpp3Dom item = items.getChild( 0 ); + assertEquals( "ooopise", item.getValue() ); + } + + @Test + public void selfOverrideOnRootNode() + { + // Todo: This does not work when loaded. Probably a bug related to null vs "" handling + // Xpp3Dom t1 = build( "" ); + + Xpp3Dom t1 = new Xpp3Dom( "top" ); + t1.setAttribute( "attr", "value" ); + + t1.setAttribute( Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.SELF_COMBINATION_OVERRIDE ); + + Xpp3Dom t2 = build( "val2" ); + Xpp3Dom result = mergeXpp3Dom( t1, t2 ); + + assertEquals( 2, result.getAttributeNames().length ); + assertNull( result.getValue() ); + } + + @Test + public void mergeValuesOnRootNode() + { + Xpp3Dom t1 = build( "" ); + Xpp3Dom t2 = build( "t2Val" ); + Xpp3Dom result = mergeXpp3Dom( t1, t2 ); + assertEquals( 2, result.getAttributeNames().length ); + assertEquals( result.getValue(), t2.getValue() ); + } + + @Test + public void mergeAttributesOnRootNode() + { + Xpp3Dom t1 = build( "" ); + Xpp3Dom t2 = build( "" ); + + Xpp3Dom dom = mergeXpp3Dom( t1, t2 ); + assertEquals( 3, dom.getAttributeNames().length ); + } + + @Test + public void combineAppend() + { + Xpp3Dom t1 = new Xpp3Dom( "root" ); + t1.setAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.CHILDREN_COMBINATION_APPEND ); + t1.addChild( createElement( "sub", "s1Value" ) ); + + Xpp3Dom t2 = new Xpp3Dom( "root" ); + t2.addChild( createElement( "sub", "s1Value" ) ); + + Xpp3Dom result = mergeXpp3Dom( t1, t2 ); + + assertEquals( 2, result.getChildren( "sub" ).length ); + } + + @Test + public void mergeOverride() + { + Xpp3Dom t1 = new Xpp3Dom( "root" ); + t1.setAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.CHILDREN_COMBINATION_APPEND ); + t1.addChild( createElement( "sub", "s1Value" ) ); + + Xpp3Dom t2 = new Xpp3Dom( "root" ); + t2.addChild( createElement( "sub", "s1Value" ) ); + + Xpp3Dom result = mergeXpp3Dom( t1, t2, Boolean.TRUE ); + + assertEquals( 1, result.getChildren( "sub" ).length ); + } + + + @Test( expected = NullPointerException.class ) + public void nullValue() + { + //noinspection ConstantConditions + new Xpp3Dom( "top" ).setAttribute( null, "value" ); + } + + @Test( expected = NullPointerException.class ) + public void nullAttribute() + { + //noinspection ConstantConditions + new Xpp3Dom( "root" ).setAttribute( "attr", null ); + } + + + @Test + public void testEquals() + { + Xpp3Dom dom = new Xpp3Dom( "single" ); + dom.addChild( new Xpp3Dom( "kid" ) ); + + Xpp3Dom other = new Xpp3Dom( "single" ); + other.addChild( new Xpp3Dom( "kid2" ) ); + + assertEquals( dom, dom ); + //noinspection ObjectEqualsNull + assertFalse( dom.equals( null ) ); + assertFalse( dom.equals( new Xpp3Dom( (String) null ) ) ); + assertFalse( dom.equals( other ) ); + } + + @Test + public void dominantWinsCollections() + throws XmlPullParserException + { + Xpp3Dom parent = build( "unodos" ); + Xpp3Dom dominant = build( "tres" ); + + Xpp3Dom result = mergeXpp3Dom( dominant, parent ); + + Xpp3Dom items = result.getChild( "entries" ); + assertEquals( 1, items.getChildCount() ); + + assertElemEquals( "tres", items.getChild( 0 ) ); + } + + @Test + public void combineChildrenAppendTest() + throws XmlPullParserException + { + Xpp3Dom parent = + build( "unodostres" ); + Xpp3Dom child = build( "quatro" ); + + Xpp3Dom result = mergeXpp3Dom( child, parent ); + + Xpp3Dom items = result.getChild( "entries" ); + assertEquals( 4, items.getChildCount() ); + + Xpp3Dom[] item = items.getChildren(); + + assertElemEquals( "uno", item[0] ); + assertElemEquals( "dos", item[1] ); + assertElemEquals( "tres", item[2] ); + assertElemEquals( "quatro", item[3] ); + } + + @Test + public void unchangedWithFirstOrLastEmpty() + throws Exception + { + String configStr = "test"; + Xpp3Dom dominant = build( configStr ); + Xpp3Dom duplicatedDominant = build( configStr ); + validateEntries( dominant ); + Xpp3Dom result = mergeXpp3Dom( dominant, duplicatedDominant ); + validateEntries( result ); + } + + private void validateEntries( Xpp3Dom result ) + { + Xpp3Dom entries = result.getChild( "entries" ); + assertEquals( 3, entries.getChildCount() ); + assertXpp3Null( entries.getChild( 0 ) ); + assertEquals( "test", entries.getChild( 1 ).getValue() ); + assertXpp3Null( entries.getChild( 2 ) ); + } + + + static void assertElemEquals( String value, Xpp3Dom element ) + { + assertEquals( value, element.getValue() ); + } + + + void assertXpp3Null( Xpp3Dom entry ) + { + // Todo: When we used xpp3dom, all methods using this assert used to return null + assertEquals( "", entry.getValue() ); + } + + @Test + public void recessiveChildrenIncludedWhenDominantEmpty() + throws Exception + { + String dominant = "bazzy"; + String recessive = "barry"; + + Xpp3Dom merged = mergeXpp3Dom( build( dominant ), build( recessive ) ); + + assertEquals( 2, merged.getChildCount() ); + assertEquals( "bazzy", merged.getChild( "baz" ).getValue() ); + assertEquals( "barry", merged.getChild( "bar" ).getValue() ); + } + + static Xpp3Dom build( String stringContent ) + { + return Xpp3DomBuilder.build( new StringReader( stringContent ) ); + } + + @Test + public void duplicatedChildren() + throws IOException, XmlPullParserException + { + String dupes = "xy"; + assertEquals( "y", build( dupes ).getChild( "baz" ).getValue() ); + } + +} diff --git a/Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory1/file1.txt b/Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory1/file1.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory2/directory21/file21.txt b/Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory2/directory21/file21.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory2/file2.txt b/Java-base/maven-shared-utils/src/src/test/resources/directorywalker/directory2/file2.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Java-base/maven-shared-utils/src/src/test/resources/directorywalker/file.txt b/Java-base/maven-shared-utils/src/src/test/resources/directorywalker/file.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Java-base/maven-shared-utils/src/src/test/resources/executable b/Java-base/maven-shared-utils/src/src/test/resources/executable new file mode 100755 index 000000000..e804f19a0 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/executable @@ -0,0 +1 @@ +noop diff --git a/Java-base/maven-shared-utils/src/src/test/resources/expand/expand_test.zip b/Java-base/maven-shared-utils/src/src/test/resources/expand/expand_test.zip new file mode 100644 index 0000000000000000000000000000000000000000..efb337f9f1f1eeb19bb4c3c3182b46b26a88f842 GIT binary patch literal 364 zcmWIWW@h1H0D+c3HhVAwO7JkqFr-!#B<7{Wm!uY#=!b@IGBAgKxAB_|#HAJ742&#a z85tN@M1bl8P)zv}l6!>*C6LW?ascfI3)!;jJTxiRx%ph@;U=M>i4G$q*BPo?}?j=!Rw@ng?(h&&mdJ3KI}+ K1ky`E90mYICrXq6 literal 0 HcmV?d00001 diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/dirOnTheOutside/FileInDirOnTheOutside.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/dirOnTheOutside/FileInDirOnTheOutside.txt new file mode 100644 index 000000000..ee541c34f --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/dirOnTheOutside/FileInDirOnTheOutside.txt @@ -0,0 +1,19 @@ +/* + * 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. + */ +I am on the outside... diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/onTheOutside.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/onTheOutside.txt new file mode 100644 index 000000000..8d116b3c7 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/onTheOutside.txt @@ -0,0 +1,20 @@ +/* + * 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. + */ + +I am on the outside of the src diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/regen.sh b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/regen.sh new file mode 100755 index 000000000..a4e3cfa09 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/regen.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# 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. +rm symlinks.zip +rm symlinks.tar +cd src +zip --symlinks ../symlinks.zip file* targetDir sym* +tar -cvf ../symlinks.tar file* targetDir sym* + diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/aRegularDir/aRegularFile.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/aRegularDir/aRegularFile.txt new file mode 100644 index 000000000..1d5f805c5 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/aRegularDir/aRegularFile.txt @@ -0,0 +1,19 @@ +/* + * 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. + */ +I am just an ordinary file diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileR.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileR.txt new file mode 100644 index 000000000..afde6ecd7 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileR.txt @@ -0,0 +1,20 @@ +/* + * 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. + */ + +This file is a source. r r r filemode diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileW.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileW.txt new file mode 100644 index 000000000..e07e28e33 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileW.txt @@ -0,0 +1,19 @@ +/* + * 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. + */ +all w diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileX.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileX.txt new file mode 100755 index 000000000..a9a85d55f --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/fileX.txt @@ -0,0 +1,19 @@ +/* + * 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. + */ +xxx diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symDir/targetFile.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symDir/targetFile.txt new file mode 100644 index 000000000..3d7ad979b --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symDir/targetFile.txt @@ -0,0 +1,19 @@ +/* + * 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. + */ +This is a target file diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symLinkToDirOnTheOutside/FileInDirOnTheOutside.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symLinkToDirOnTheOutside/FileInDirOnTheOutside.txt new file mode 100644 index 000000000..ee541c34f --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symLinkToDirOnTheOutside/FileInDirOnTheOutside.txt @@ -0,0 +1,19 @@ +/* + * 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. + */ +I am on the outside... diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symLinkToFileOnTheOutside b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symLinkToFileOnTheOutside new file mode 100644 index 000000000..8d116b3c7 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symLinkToFileOnTheOutside @@ -0,0 +1,20 @@ +/* + * 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. + */ + +I am on the outside of the src diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symR b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symR new file mode 100644 index 000000000..afde6ecd7 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symR @@ -0,0 +1,20 @@ +/* + * 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. + */ + +This file is a source. r r r filemode diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symW b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symW new file mode 100644 index 000000000..e07e28e33 --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symW @@ -0,0 +1,19 @@ +/* + * 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. + */ +all w diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symX b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symX new file mode 100755 index 000000000..a9a85d55f --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/symX @@ -0,0 +1,19 @@ +/* + * 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. + */ +xxx diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/targetDir/targetFile.txt b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/targetDir/targetFile.txt new file mode 100644 index 000000000..3d7ad979b --- /dev/null +++ b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/src/targetDir/targetFile.txt @@ -0,0 +1,19 @@ +/* + * 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. + */ +This is a target file diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/symlinks.tar b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/symlinks.tar new file mode 100644 index 0000000000000000000000000000000000000000..c40706069c57c41ecbaed7982ba149672dc9f163 GIT binary patch literal 13824 zcmeHNPfy!06c3ZoG_I4T9W(9P1lzG4$gQm9v1A7i(Dn|+jh zh8_2uKTQ*~(z3WMs*O+zQXWRSs#bq!Qr*<+)3^fTM#3?lmgs{ty{6&bQ zvdkgmy2!+YV&uS!9GeJJg0W#DHQlMUI*t=Rf-c=eL7W8ssQR>&;I+%kQhb!lRLPV> zpyW$QyYF5buMFdh-#1Ubn&)PoWSQ{>{)u7i{DNPQcm6DBM2tm8^5M zIW9Z!3III}PO5@k8~PASBqKZbW7*7Ln%NL@%)2a@#( zl63^hYD3EGiXD0v@m-hSE9RCxd{eLBfq4{4D=#&`OvffxT|Eu(UX~UBL=lMT8e#HM2Y5!k^ z|HC@jyx07`ilN@fZxsB0u9p8@#x(zLqycFD-+KO6>HE^=f&b_FKSJ~o|2tgs|0?E- z$z-D0e-#70q2G}F4*~Eik#vGcQ6Hc3f6Cq5|FJ`eo5ugStNmXQ9MxtQ;s0HzBIEyV zwd%j5nfSk~0daogXYJiQ{x8cHi2wip{N2p?`H?qz6;a1(oFV}d{^&fT|xC6#u|Hm+e z`yZs7oYalomaqQ+|IfkbrWb-0oQ!(c;$)n}!9X-Q;7j072m<^GT>pdkAF=Iy*s9HT ztNC4u#y`Vf4fLrQErEaL{{#~b_dg{5;fzf-yDP~pT;reL)ojw^t8FcfKc)=9{*TC@ zjmnG3vYQ(JvnpE6OV7a{Oa7PmtG=9~@jq`K@oJ0D!XNg3DgLStC$}8`DpFd~BcMlM Hn?~R-y5H2I literal 0 HcmV?d00001 diff --git a/Java-base/maven-shared-utils/src/src/test/resources/symlinks/symlinks.zip b/Java-base/maven-shared-utils/src/src/test/resources/symlinks/symlinks.zip new file mode 100644 index 0000000000000000000000000000000000000000..309a853d75b3c2af122666cf71482a3f41e479ce GIT binary patch literal 1616 zcmWIWW@Zs#U|`^2nAl|Q`tzj4F-0Ix4Tw1zWEj#ib5eu!N-9c1LpT|jH#RDU9NMB9 zQd+^yz{v7~nSlYUmHXrgoiLBnXFNk0E_HGp{khcjuj00bu>I zn^Z$U7%k8cwjfM8ztqZg>IR!`VW24>ECDgeCo?ZQBp+y=e_lvNs()!oab`*?*z8td zWPmW5*?M~VDX6OT5te|QyMNiv#V|{Rku5+~MFyP4y)tHxyg00bv2`6L>I3=MPV`0j?6U~?iLdJm8QGhoilN>Xy+#vz<90LO* z5HD#2vEZHv4Pk}k6|`u97>6q(AdK7Am;yBpnk|Sk4mCRf_)3hBzSy_nMGjcF)+McXaNjEScU<*4NGQ04mr$Bf^1e3#4H?H z2535#?1F4MX6`^X{W-Gf$a#gZvq1)5a0L&!BnS`1%kWK1?m;}rx#F>Pe gIgm}d3NZ Date: Thu, 30 Nov 2023 20:44:39 +0900 Subject: [PATCH 2/2] refactor(java): add shared utils --- .../Dockerfile | 18 + .../buggy.java | 137 + .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 515 ++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 921 ++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 900 ++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 900 ++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 932 ++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2142 ++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2141 ++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2141 ++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../maven-shared-utils-IOUtil_1040/Dockerfile | 18 + .../maven-shared-utils-IOUtil_1040/buggy.java | 1456 ++++++++++ .../metadata.json | 21 + Java/maven-shared-utils-IOUtil_1040/npe.json | 7 + .../maven-shared-utils-IOUtil_1146/Dockerfile | 18 + .../maven-shared-utils-IOUtil_1146/buggy.java | 1456 ++++++++++ .../metadata.json | 21 + Java/maven-shared-utils-IOUtil_1146/npe.json | 7 + .../maven-shared-utils-IOUtil_1252/Dockerfile | 18 + .../maven-shared-utils-IOUtil_1252/buggy.java | 1456 ++++++++++ .../metadata.json | 21 + Java/maven-shared-utils-IOUtil_1252/npe.json | 7 + .../maven-shared-utils-IOUtil_1358/Dockerfile | 18 + .../maven-shared-utils-IOUtil_1358/buggy.java | 1456 ++++++++++ .../metadata.json | 21 + Java/maven-shared-utils-IOUtil_1358/npe.json | 7 + Java/maven-shared-utils-Os_311/Dockerfile | 18 + Java/maven-shared-utils-Os_311/buggy.java | 406 +++ Java/maven-shared-utils-Os_311/metadata.json | 21 + Java/maven-shared-utils-Os_311/npe.json | 7 + Java/maven-shared-utils-Os_383/Dockerfile | 18 + Java/maven-shared-utils-Os_383/buggy.java | 406 +++ Java/maven-shared-utils-Os_383/metadata.json | 21 + Java/maven-shared-utils-Os_383/npe.json | 7 + Java/maven-shared-utils-Os_387/Dockerfile | 18 + Java/maven-shared-utils-Os_387/buggy.java | 406 +++ Java/maven-shared-utils-Os_387/metadata.json | 21 + Java/maven-shared-utils-Os_387/npe.json | 7 + Java/maven-shared-utils-Os_391/Dockerfile | 18 + Java/maven-shared-utils-Os_391/buggy.java | 406 +++ Java/maven-shared-utils-Os_391/metadata.json | 21 + Java/maven-shared-utils-Os_391/npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 360 +++ .../metadata.json | 21 + Java/maven-shared-utils-PathTool_270/npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 387 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 223 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 225 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 225 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 223 ++ .../metadata.json | 21 + .../npe.json | 7 + Java/maven-shared-utils-Shell_152/Dockerfile | 18 + Java/maven-shared-utils-Shell_152/buggy.java | 412 +++ .../metadata.json | 21 + Java/maven-shared-utils-Shell_152/npe.json | 7 + Java/maven-shared-utils-Shell_155/Dockerfile | 18 + Java/maven-shared-utils-Shell_155/buggy.java | 412 +++ .../metadata.json | 21 + Java/maven-shared-utils-Shell_155/npe.json | 7 + Java/maven-shared-utils-Shell_380/Dockerfile | 18 + Java/maven-shared-utils-Shell_380/buggy.java | 423 +++ .../metadata.json | 21 + Java/maven-shared-utils-Shell_380/npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2556 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2553 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2554 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2551 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2559 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2557 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2550 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2550 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2548 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2555 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2555 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2562 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2562 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2559 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2557 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2559 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2558 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2559 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2557 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2566 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2560 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2570 +++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2553 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2555 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2548 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2566 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2559 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2556 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2563 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2560 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2563 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 2561 ++++++++++++++++ .../metadata.json | 21 + .../npe.json | 7 + Java/maven-shared-utils-Style_117/Dockerfile | 18 + Java/maven-shared-utils-Style_117/buggy.java | 173 ++ .../metadata.json | 21 + Java/maven-shared-utils-Style_117/npe.json | 7 + Java/maven-shared-utils-Style_128/Dockerfile | 18 + Java/maven-shared-utils-Style_128/buggy.java | 173 ++ .../metadata.json | 21 + Java/maven-shared-utils-Style_128/npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 207 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 205 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 162 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 154 + .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 101 + .../metadata.json | 21 + .../npe.json | 7 + .../maven-shared-utils-Xpp3Dom_177/Dockerfile | 18 + .../maven-shared-utils-Xpp3Dom_177/buggy.java | 436 +++ .../metadata.json | 21 + Java/maven-shared-utils-Xpp3Dom_177/npe.json | 7 + .../maven-shared-utils-Xpp3Dom_186/Dockerfile | 18 + .../maven-shared-utils-Xpp3Dom_186/buggy.java | 434 +++ .../metadata.json | 21 + Java/maven-shared-utils-Xpp3Dom_186/npe.json | 7 + .../maven-shared-utils-Xpp3Dom_190/Dockerfile | 18 + .../maven-shared-utils-Xpp3Dom_190/buggy.java | 434 +++ .../metadata.json | 21 + Java/maven-shared-utils-Xpp3Dom_190/npe.json | 7 + .../maven-shared-utils-Xpp3Dom_351/Dockerfile | 18 + .../maven-shared-utils-Xpp3Dom_351/buggy.java | 424 +++ .../metadata.json | 21 + Java/maven-shared-utils-Xpp3Dom_351/npe.json | 7 + .../maven-shared-utils-Xpp3Dom_352/Dockerfile | 18 + .../maven-shared-utils-Xpp3Dom_352/buggy.java | 424 +++ .../metadata.json | 21 + Java/maven-shared-utils-Xpp3Dom_352/npe.json | 7 + 280 files changed, 109455 insertions(+) create mode 100644 Java/maven-shared-utils-BourneShell_102/Dockerfile create mode 100644 Java/maven-shared-utils-BourneShell_102/buggy.java create mode 100644 Java/maven-shared-utils-BourneShell_102/metadata.json create mode 100644 Java/maven-shared-utils-BourneShell_102/npe.json create mode 100644 Java/maven-shared-utils-Commandline_408/Dockerfile create mode 100644 Java/maven-shared-utils-Commandline_408/buggy.java create mode 100644 Java/maven-shared-utils-Commandline_408/metadata.json create mode 100644 Java/maven-shared-utils-Commandline_408/npe.json create mode 100644 Java/maven-shared-utils-DirectoryScanner_403/Dockerfile create mode 100644 Java/maven-shared-utils-DirectoryScanner_403/buggy.java create mode 100644 Java/maven-shared-utils-DirectoryScanner_403/metadata.json create mode 100644 Java/maven-shared-utils-DirectoryScanner_403/npe.json create mode 100644 Java/maven-shared-utils-DirectoryScanner_602/Dockerfile create mode 100644 Java/maven-shared-utils-DirectoryScanner_602/buggy.java create mode 100644 Java/maven-shared-utils-DirectoryScanner_602/metadata.json create mode 100644 Java/maven-shared-utils-DirectoryScanner_602/npe.json create mode 100644 Java/maven-shared-utils-DirectoryScanner_686/Dockerfile create mode 100644 Java/maven-shared-utils-DirectoryScanner_686/buggy.java create mode 100644 Java/maven-shared-utils-DirectoryScanner_686/metadata.json create mode 100644 Java/maven-shared-utils-DirectoryScanner_686/npe.json create mode 100644 Java/maven-shared-utils-DirectoryScanner_879/Dockerfile create mode 100644 Java/maven-shared-utils-DirectoryScanner_879/buggy.java create mode 100644 Java/maven-shared-utils-DirectoryScanner_879/metadata.json create mode 100644 Java/maven-shared-utils-DirectoryScanner_879/npe.json create mode 100644 Java/maven-shared-utils-FileUtils_1348/Dockerfile create mode 100644 Java/maven-shared-utils-FileUtils_1348/buggy.java create mode 100644 Java/maven-shared-utils-FileUtils_1348/metadata.json create mode 100644 Java/maven-shared-utils-FileUtils_1348/npe.json create mode 100644 Java/maven-shared-utils-FileUtils_1553/Dockerfile create mode 100644 Java/maven-shared-utils-FileUtils_1553/buggy.java create mode 100644 Java/maven-shared-utils-FileUtils_1553/metadata.json create mode 100644 Java/maven-shared-utils-FileUtils_1553/npe.json create mode 100644 Java/maven-shared-utils-FileUtils_1558/Dockerfile create mode 100644 Java/maven-shared-utils-FileUtils_1558/buggy.java create mode 100644 Java/maven-shared-utils-FileUtils_1558/metadata.json create mode 100644 Java/maven-shared-utils-FileUtils_1558/npe.json create mode 100644 Java/maven-shared-utils-IOUtil_1040/Dockerfile create mode 100644 Java/maven-shared-utils-IOUtil_1040/buggy.java create mode 100644 Java/maven-shared-utils-IOUtil_1040/metadata.json create mode 100644 Java/maven-shared-utils-IOUtil_1040/npe.json create mode 100644 Java/maven-shared-utils-IOUtil_1146/Dockerfile create mode 100644 Java/maven-shared-utils-IOUtil_1146/buggy.java create mode 100644 Java/maven-shared-utils-IOUtil_1146/metadata.json create mode 100644 Java/maven-shared-utils-IOUtil_1146/npe.json create mode 100644 Java/maven-shared-utils-IOUtil_1252/Dockerfile create mode 100644 Java/maven-shared-utils-IOUtil_1252/buggy.java create mode 100644 Java/maven-shared-utils-IOUtil_1252/metadata.json create mode 100644 Java/maven-shared-utils-IOUtil_1252/npe.json create mode 100644 Java/maven-shared-utils-IOUtil_1358/Dockerfile create mode 100644 Java/maven-shared-utils-IOUtil_1358/buggy.java create mode 100644 Java/maven-shared-utils-IOUtil_1358/metadata.json create mode 100644 Java/maven-shared-utils-IOUtil_1358/npe.json create mode 100644 Java/maven-shared-utils-Os_311/Dockerfile create mode 100644 Java/maven-shared-utils-Os_311/buggy.java create mode 100644 Java/maven-shared-utils-Os_311/metadata.json create mode 100644 Java/maven-shared-utils-Os_311/npe.json create mode 100644 Java/maven-shared-utils-Os_383/Dockerfile create mode 100644 Java/maven-shared-utils-Os_383/buggy.java create mode 100644 Java/maven-shared-utils-Os_383/metadata.json create mode 100644 Java/maven-shared-utils-Os_383/npe.json create mode 100644 Java/maven-shared-utils-Os_387/Dockerfile create mode 100644 Java/maven-shared-utils-Os_387/buggy.java create mode 100644 Java/maven-shared-utils-Os_387/metadata.json create mode 100644 Java/maven-shared-utils-Os_387/npe.json create mode 100644 Java/maven-shared-utils-Os_391/Dockerfile create mode 100644 Java/maven-shared-utils-Os_391/buggy.java create mode 100644 Java/maven-shared-utils-Os_391/metadata.json create mode 100644 Java/maven-shared-utils-Os_391/npe.json create mode 100644 Java/maven-shared-utils-PathTool_270/Dockerfile create mode 100644 Java/maven-shared-utils-PathTool_270/buggy.java create mode 100644 Java/maven-shared-utils-PathTool_270/metadata.json create mode 100644 Java/maven-shared-utils-PathTool_270/npe.json create mode 100644 Java/maven-shared-utils-PrettyPrintXMLWriter_365/Dockerfile create mode 100644 Java/maven-shared-utils-PrettyPrintXMLWriter_365/buggy.java create mode 100644 Java/maven-shared-utils-PrettyPrintXMLWriter_365/metadata.json create mode 100644 Java/maven-shared-utils-PrettyPrintXMLWriter_365/npe.json create mode 100644 Java/maven-shared-utils-PropertyUtils_106/Dockerfile create mode 100644 Java/maven-shared-utils-PropertyUtils_106/buggy.java create mode 100644 Java/maven-shared-utils-PropertyUtils_106/metadata.json create mode 100644 Java/maven-shared-utils-PropertyUtils_106/npe.json create mode 100644 Java/maven-shared-utils-PropertyUtils_141/Dockerfile create mode 100644 Java/maven-shared-utils-PropertyUtils_141/buggy.java create mode 100644 Java/maven-shared-utils-PropertyUtils_141/metadata.json create mode 100644 Java/maven-shared-utils-PropertyUtils_141/npe.json create mode 100644 Java/maven-shared-utils-PropertyUtils_170/Dockerfile create mode 100644 Java/maven-shared-utils-PropertyUtils_170/buggy.java create mode 100644 Java/maven-shared-utils-PropertyUtils_170/metadata.json create mode 100644 Java/maven-shared-utils-PropertyUtils_170/npe.json create mode 100644 Java/maven-shared-utils-PropertyUtils_202/Dockerfile create mode 100644 Java/maven-shared-utils-PropertyUtils_202/buggy.java create mode 100644 Java/maven-shared-utils-PropertyUtils_202/metadata.json create mode 100644 Java/maven-shared-utils-PropertyUtils_202/npe.json create mode 100644 Java/maven-shared-utils-Shell_152/Dockerfile create mode 100644 Java/maven-shared-utils-Shell_152/buggy.java create mode 100644 Java/maven-shared-utils-Shell_152/metadata.json create mode 100644 Java/maven-shared-utils-Shell_152/npe.json create mode 100644 Java/maven-shared-utils-Shell_155/Dockerfile create mode 100644 Java/maven-shared-utils-Shell_155/buggy.java create mode 100644 Java/maven-shared-utils-Shell_155/metadata.json create mode 100644 Java/maven-shared-utils-Shell_155/npe.json create mode 100644 Java/maven-shared-utils-Shell_380/Dockerfile create mode 100644 Java/maven-shared-utils-Shell_380/buggy.java create mode 100644 Java/maven-shared-utils-Shell_380/metadata.json create mode 100644 Java/maven-shared-utils-Shell_380/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1340/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1340/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1340/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1340/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1346/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1346/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1346/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1346/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1375/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1375/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1375/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1375/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1384/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1384/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1384/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1384/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1413/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1413/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1413/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1413/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1429/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1429/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1429/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1429/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1447/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1447/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1447/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1447/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1479/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1479/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1479/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1479/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1513/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1513/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1513/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1513/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1570/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1570/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1570/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1570/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1611/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1611/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1611/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1611/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1670/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1670/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1670/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1670/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1702/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1702/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1702/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1702/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1730/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1730/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1730/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1730/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1756/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1756/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1756/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1756/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1784/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1784/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1784/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1784/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1811/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1811/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1811/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1811/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1839/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1839/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1839/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1839/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1865/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1865/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1865/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1865/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1914/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1914/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1914/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1914/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_1930/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_1930/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_1930/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_1930/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_228/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_228/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_228/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_228/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_2298/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_2298/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_2298/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_2298/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_2348/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_2348/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_2348/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_2348/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_2425/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_2425/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_2425/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_2425/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_246/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_246/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_246/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_246/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_331/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_331/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_331/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_331/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_369/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_369/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_369/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_369/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_489/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_489/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_489/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_489/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_552/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_552/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_552/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_552/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_82/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_82/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_82/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_82/npe.json create mode 100644 Java/maven-shared-utils-StringUtils_96/Dockerfile create mode 100644 Java/maven-shared-utils-StringUtils_96/buggy.java create mode 100644 Java/maven-shared-utils-StringUtils_96/metadata.json create mode 100644 Java/maven-shared-utils-StringUtils_96/npe.json create mode 100644 Java/maven-shared-utils-Style_117/Dockerfile create mode 100644 Java/maven-shared-utils-Style_117/buggy.java create mode 100644 Java/maven-shared-utils-Style_117/metadata.json create mode 100644 Java/maven-shared-utils-Style_117/npe.json create mode 100644 Java/maven-shared-utils-Style_128/Dockerfile create mode 100644 Java/maven-shared-utils-Style_128/buggy.java create mode 100644 Java/maven-shared-utils-Style_128/metadata.json create mode 100644 Java/maven-shared-utils-Style_128/npe.json create mode 100644 Java/maven-shared-utils-XmlStreamReaderTest_63/Dockerfile create mode 100644 Java/maven-shared-utils-XmlStreamReaderTest_63/buggy.java create mode 100644 Java/maven-shared-utils-XmlStreamReaderTest_63/metadata.json create mode 100644 Java/maven-shared-utils-XmlStreamReaderTest_63/npe.json create mode 100644 Java/maven-shared-utils-XmlStreamReaderTest_80/Dockerfile create mode 100644 Java/maven-shared-utils-XmlStreamReaderTest_80/buggy.java create mode 100644 Java/maven-shared-utils-XmlStreamReaderTest_80/metadata.json create mode 100644 Java/maven-shared-utils-XmlStreamReaderTest_80/npe.json create mode 100644 Java/maven-shared-utils-Xpp3DomUtils_141/Dockerfile create mode 100644 Java/maven-shared-utils-Xpp3DomUtils_141/buggy.java create mode 100644 Java/maven-shared-utils-Xpp3DomUtils_141/metadata.json create mode 100644 Java/maven-shared-utils-Xpp3DomUtils_141/npe.json create mode 100644 Java/maven-shared-utils-Xpp3DomUtils_89/Dockerfile create mode 100644 Java/maven-shared-utils-Xpp3DomUtils_89/buggy.java create mode 100644 Java/maven-shared-utils-Xpp3DomUtils_89/metadata.json create mode 100644 Java/maven-shared-utils-Xpp3DomUtils_89/npe.json create mode 100644 Java/maven-shared-utils-Xpp3DomWriter_82/Dockerfile create mode 100644 Java/maven-shared-utils-Xpp3DomWriter_82/buggy.java create mode 100644 Java/maven-shared-utils-Xpp3DomWriter_82/metadata.json create mode 100644 Java/maven-shared-utils-Xpp3DomWriter_82/npe.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_177/Dockerfile create mode 100644 Java/maven-shared-utils-Xpp3Dom_177/buggy.java create mode 100644 Java/maven-shared-utils-Xpp3Dom_177/metadata.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_177/npe.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_186/Dockerfile create mode 100644 Java/maven-shared-utils-Xpp3Dom_186/buggy.java create mode 100644 Java/maven-shared-utils-Xpp3Dom_186/metadata.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_186/npe.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_190/Dockerfile create mode 100644 Java/maven-shared-utils-Xpp3Dom_190/buggy.java create mode 100644 Java/maven-shared-utils-Xpp3Dom_190/metadata.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_190/npe.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_351/Dockerfile create mode 100644 Java/maven-shared-utils-Xpp3Dom_351/buggy.java create mode 100644 Java/maven-shared-utils-Xpp3Dom_351/metadata.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_351/npe.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_352/Dockerfile create mode 100644 Java/maven-shared-utils-Xpp3Dom_352/buggy.java create mode 100644 Java/maven-shared-utils-Xpp3Dom_352/metadata.json create mode 100644 Java/maven-shared-utils-Xpp3Dom_352/npe.json diff --git a/Java/maven-shared-utils-BourneShell_102/Dockerfile b/Java/maven-shared-utils-BourneShell_102/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-BourneShell_102/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-BourneShell_102/buggy.java b/Java/maven-shared-utils-BourneShell_102/buggy.java new file mode 100644 index 000000000..99e604619 --- /dev/null +++ b/Java/maven-shared-utils-BourneShell_102/buggy.java @@ -0,0 +1,137 @@ +package org.apache.maven.shared.utils.cli.shell; + +/* + * 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. + */ + + +import java.util.ArrayList; +import java.util.List; +import org.apache.maven.shared.utils.Os; + +/** + * @author Jason van Zyl + */ +public class BourneShell + extends Shell +{ + + /** + * Create instance of BourneShell. + */ + public BourneShell() + { + setUnconditionalQuoting( true ); + setShellCommand( "/bin/sh" ); + setArgumentQuoteDelimiter( '\'' ); + setExecutableQuoteDelimiter( '\'' ); + setSingleQuotedArgumentEscaped( true ); + setSingleQuotedExecutableEscaped( false ); + setQuotedExecutableEnabled( true ); + } + + /** + * {@inheritDoc} + */ + public String getExecutable() + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + return super.getExecutable(); + } + + return quoteOneItem( super.getExecutable(), true ); + } + + /** {@inheritDoc} */ + public List getShellArgsList() + { + List shellArgs = new ArrayList(); + List existingShellArgs = super.getShellArgsList(); + + if ( ( existingShellArgs != null ) && !existingShellArgs.isEmpty() ) + { + shellArgs.addAll( existingShellArgs ); + } + + shellArgs.add( "-c" ); + + return shellArgs; + } + + /** {@inheritDoc} */ + public String[] getShellArgs() + { + String[] shellArgs = super.getShellArgs(); + if ( shellArgs == null ) + { + shellArgs = new String[0]; + } + + if ( ( shellArgs.length > 0 ) && !shellArgs[shellArgs.length - 1].equals( "-c" ) ) + { + String[] newArgs = new String[shellArgs.length + 1]; + + System.arraycopy( shellArgs, 0, newArgs, 0, shellArgs.length ); + newArgs[shellArgs.length] = "-c"; + + shellArgs = newArgs; + } + + return shellArgs; + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +protected java.lang.String getExecutionPreamble() { + { + java.lang.String dir = /* NPEX_NULL_EXP */ + getWorkingDirectoryAsString(); + return ("cd " + quoteOneItem(dir, false)) + " && "; + } +} + + /** + *

          Unify quotes in a path for the Bourne Shell.

          + *

          + *

          +     * BourneShell.quoteOneItem(null)                       = null
          +     * BourneShell.quoteOneItem("")                         = ''
          +     * BourneShell.quoteOneItem("/test/quotedpath'abc")     = '/test/quotedpath'"'"'abc'
          +     * BourneShell.quoteOneItem("/test/quoted path'abc")    = '/test/quoted pat'"'"'habc'
          +     * BourneShell.quoteOneItem("/test/quotedpath\"abc")    = '/test/quotedpath"abc'
          +     * BourneShell.quoteOneItem("/test/quoted path\"abc")   = '/test/quoted path"abc'
          +     * BourneShell.quoteOneItem("/test/quotedpath\"'abc")   = '/test/quotedpath"'"'"'abc'
          +     * BourneShell.quoteOneItem("/test/quoted path\"'abc")  = '/test/quoted path"'"'"'abc'
          +     * 
          + * + * @param path not null path. + * @return the path unified correctly for the Bourne shell. + */ + protected String quoteOneItem( String path, boolean isExecutable ) + { + if ( path == null ) + { + return null; + } + + return "'" + path.replace( "'", "'\"'\"'" ) + "'"; + } +} diff --git a/Java/maven-shared-utils-BourneShell_102/metadata.json b/Java/maven-shared-utils-BourneShell_102/metadata.json new file mode 100644 index 000000000..41719b5df --- /dev/null +++ b/Java/maven-shared-utils-BourneShell_102/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-BourneShell_102", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java", + "line": 106, + "npe_method": "getExecutionPreamble", + "deref_field": "getWorkingDirectoryAsString", + "npe_class": "BourneShell", + "repo": "maven-shared-utils", + "bug_id": "BourneShell_102" + } +} diff --git a/Java/maven-shared-utils-BourneShell_102/npe.json b/Java/maven-shared-utils-BourneShell_102/npe.json new file mode 100644 index 000000000..52c01eede --- /dev/null +++ b/Java/maven-shared-utils-BourneShell_102/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java", + "line": 106, + "npe_method": "getExecutionPreamble", + "deref_field": "getWorkingDirectoryAsString", + "npe_class": "BourneShell" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Commandline_408/Dockerfile b/Java/maven-shared-utils-Commandline_408/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Commandline_408/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Commandline_408/buggy.java b/Java/maven-shared-utils-Commandline_408/buggy.java new file mode 100644 index 000000000..0d6c99354 --- /dev/null +++ b/Java/maven-shared-utils-Commandline_408/buggy.java @@ -0,0 +1,515 @@ +package org.apache.maven.shared.utils.cli; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; +import org.apache.maven.shared.utils.cli.shell.BourneShell; +import org.apache.maven.shared.utils.cli.shell.CmdShell; +import org.apache.maven.shared.utils.cli.shell.CommandShell; +import org.apache.maven.shared.utils.cli.shell.Shell; + +/** + *

          + * Commandline objects help handling command lines specifying processes to + * execute. + *

          + *

          + * The class can be used to define a command line as nested elements or as a + * helper to define a command line by an application. + *

          + *

          + * + * <someelement>
          + *   <acommandline executable="/executable/to/run">
          + *     <argument value="argument 1" />
          + *     <argument line="argument_1 argument_2 argument_3" />
          + *     <argument value="argument 4" />
          + *   </acommandline>
          + * </someelement>
          + *
          + *

          + *

          + * The element someelement must provide a method + * createAcommandline which returns an instance of this class. + *

          + * + * @author thomas.haas@softwired-inc.com + * @author Stefan Bodewig + */ +public class Commandline + implements Cloneable +{ + private final List arguments = new Vector(); + + //protected Vector envVars = new Vector(); + // synchronized added to preserve synchronize of Vector class + private final Map envVars = Collections.synchronizedMap( new LinkedHashMap() ); + + private Shell shell; + + /** + * Create a new command line object. + * Shell is autodetected from operating system + * @param shell The shell instance. + */ + public Commandline( Shell shell ) + { + this.shell = shell; + } + + /** + * Create a new command line object. + * Shell is autodetected from operating system + * + * @param toProcess The command to process + */ + public Commandline( String toProcess ) throws CommandLineException + { + setDefaultShell(); + String[] tmp = CommandLineUtils.translateCommandline( toProcess ); + if ( ( tmp.length > 0 ) ) + { + setExecutable( tmp[0] ); + for ( int i = 1; i < tmp.length; i++ ) + { + createArg().setValue( tmp[i] ); + } + } + } + + /** + * Create a new command line object. + * Shell is autodetected from operating system + */ + public Commandline() + { + setDefaultShell(); + } + + /** + *

          Sets the shell or command-line interpretor for the detected operating system, + * and the shell arguments.

          + */ + private void setDefaultShell() + { + //If this is windows set the shell to command.com or cmd.exe with correct arguments. + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + if ( Os.isFamily( Os.FAMILY_WIN9X ) ) + { + setShell( new CommandShell() ); + } + else + { + setShell( new CmdShell() ); + } + } + else + { + setShell( new BourneShell() ); + } + } + + /** + * Creates an argument object. + *

          + *

          Each commandline object has at most one instance of the + * argument class. This method calls + * this.createArgument(false).

          + * + * @return the argument object. + */ + public Arg createArg() + { + return this.createArg( false ); + } + + /** + * Creates an argument object and adds it to our list of args. + *

          + *

          Each commandline object has at most one instance of the + * argument class.

          + * + * @param insertAtStart if true, the argument is inserted at the + * beginning of the list of args, otherwise it is appended. + * @return The arguments. + */ + public Arg createArg( boolean insertAtStart ) + { + Arg argument = new Argument(); + if ( insertAtStart ) + { + arguments.add( 0, argument ); + } + else + { + arguments.add( argument ); + } + return argument; + } + + /** + * Sets the executable to run. + * @param executable The executable. + */ + public void setExecutable( String executable ) + { + shell.setExecutable( executable ); + } + + /** + * @return The executable. + */ + public String getExecutable() + { + + return shell.getExecutable(); + } + + /** + * @param line The arguments. + */ + public void addArguments( String... line ) + { + for ( String aLine : line ) + { + createArg().setValue( aLine ); + } + } + + /** + * Add an environment variable + * @param name The name of the environment variable. + * @param value The appropriate value. + */ + public void addEnvironment( String name, String value ) + { + //envVars.add( name + "=" + value ); + envVars.put( name, value ); + } + + /** + * Add system environment variables + */ + public void addSystemEnvironment() + { + Properties systemEnvVars = CommandLineUtils.getSystemEnvVars(); + + for ( Object o : systemEnvVars.keySet() ) + { + String key = (String) o; + if ( !envVars.containsKey( key ) ) + { + addEnvironment( key, systemEnvVars.getProperty( key ) ); + } + } + } + + /** + * Return the list of environment variables + * @return an array of all environment variables. + */ + public String[] getEnvironmentVariables() + { + addSystemEnvironment(); + String[] environmentVars = new String[envVars.size()]; + int i = 0; + for ( String name : envVars.keySet() ) + { + String value = envVars.get( name ); + environmentVars[i] = name + "=" + value; + i++; + } + return environmentVars; + } + + /** + * Returns the executable and all defined arguments. + * @return an array of all arguments incl. executable. + */ + public String[] getCommandline() + { + final String[] args = getArguments(); + String executable = getExecutable(); + + if ( executable == null ) + { + return args; + } + final String[] result = new String[args.length + 1]; + result[0] = executable; + System.arraycopy( args, 0, result, 1, args.length ); + return result; + } + + /** + * @return the shell, executable and all defined arguments without masking any arguments. + */ + private String[] getShellCommandline() + { + return getShellCommandline( false ) ; + } + + /** + * @param mask flag to mask any arguments (having his {@code mask} field to {@code true}). + * @return the shell, executable and all defined arguments with masking some arguments if + * {@code mask} parameter is on + */ + private String[] getShellCommandline( boolean mask ) + { + List shellCommandLine = getShell().getShellCommandLine( getArguments( mask ) ); + return shellCommandLine.toArray( new String[shellCommandLine.size()] ); + } + + /** + * Returns all arguments defined by addLine, + * addValue or the argument object. + * @return an array of arguments. + */ + public String[] getArguments() + { + return getArguments( false ); + } + + /** + * Returns all arguments defined by addLine, + * addValue or the argument object. + * + * @param mask flag to mask any arguments (having his {@code mask} field to {@code true}). + * @return an array of arguments. + */ + public String[] getArguments( boolean mask ) + { + List result = new ArrayList( arguments.size() * 2 ); + for ( Arg argument : arguments ) + { + Argument arg = (Argument) argument; + String[] s = arg.getParts(); + if ( s != null ) + { + if ( mask && ( arg.isMask() ) ) + { + // should be a key-pair argument + if ( s.length > 0 ) + { + + // use a masked copy + String[] copy = new String[s.length]; + Arrays.fill( copy, "*****" ); + s = copy; + } + } + Collections.addAll( result, s ); + } + } + + return result.toArray( new String[result.size()] ); + } + + /** {@inheritDoc} + */ + public String toString() + { + return StringUtils.join( getShellCommandline( true ), " " ); + } + + + /** {@inheritDoc} + */ + public Object clone() + { + throw new RuntimeException( "Do we ever clone a commandline?" ); +/* Commandline c = new Commandline( (Shell) shell.clone() ); + c.addArguments( getArguments() ); + return c;*/ + } + + /** + * Sets working directory. + * @param path The to be set as working directory. + */ + public void setWorkingDirectory( String path ) + { + shell.setWorkingDirectory( path ); + } + + /** + * Sets execution directory. + * @param workingDirectory The working directory. + */ + public void setWorkingDirectory( File workingDirectory ) + { + shell.setWorkingDirectory( workingDirectory ); + } + + /** + * @return The working directory. + */ + public File getWorkingDirectory() + { + return shell.getWorkingDirectory(); + } + + /** + * Clear out the arguments but leave the executable in place for another operation. + */ + public void clearArgs() + { + arguments.clear(); + } + + /** + * Executes the command. + * @return The process. + * @throws CommandLineException in case of errors. + */ +/** + * Executes the command. + * + * @return The process. + * @throws CommandLineException + * in case of errors. + */ +public java.lang.Process execute() throws org.apache.maven.shared.utils.cli.CommandLineException { + java.lang.Process process; + // addEnvironment( "MAVEN_TEST_ENVAR", "MAVEN_TEST_ENVAR_VALUE" ); + java.lang.String[] environment = getEnvironmentVariables(); + java.io.File workingDir = shell.getWorkingDirectory(); + try { + { + if (!/* NPEX_NULL_EXP */ + workingDir.exists()) { + throw new org.apache.maven.shared.utils.cli.CommandLineException(("Working directory \"" + workingDir.getPath()) + "\" does not exist!"); + } else if (!workingDir.isDirectory()) { + throw new org.apache.maven.shared.utils.cli.CommandLineException(("Path \"" + workingDir.getPath()) + "\" does not specify a directory."); + } + process = java.lang.Runtime.getRuntime().exec(getShellCommandline(), environment, workingDir); + } + } catch (java.io.IOException ex) { + throw new org.apache.maven.shared.utils.cli.CommandLineException("Error while executing process.", ex); + } + return process; +} + + /** + * Allows to set the shell to be used in this command line. + * + * @param shell the shell + */ + void setShell( Shell shell ) + { + this.shell = shell; + } + + /** + * Get the shell to be used in this command line. + * @return the shell. + */ + public Shell getShell() + { + return shell; + } + + /** + * + */ + public static class Argument + implements Arg + { + private String[] parts; + + private boolean mask; + + /** + * {@inheritDoc} + */ + public void setValue( String value ) + { + if ( value != null ) + { + parts = new String[]{ value }; + } + } + + /** + * {@inheritDoc} + */ + public void setLine( String line ) throws CommandLineException + { + if ( line == null ) + { + return; + } + try + { + parts = CommandLineUtils.translateCommandline( line ); + } + catch ( CommandLineException e ) + { + System.err.println( "Error translating Commandline." ); + throw( e ); + } + } + + /** + * {@inheritDoc} + */ + public void setFile( File value ) + { + parts = new String[]{ value.getAbsolutePath() }; + } + + /** + * {@inheritDoc} + */ + public void setMask( boolean mask ) + { + this.mask = mask; + } + + /** + * @return The parts. + */ + private String[] getParts() + { + return parts; + } + + /** + * @return true/false + */ + public boolean isMask() + { + return mask; + } + } +} diff --git a/Java/maven-shared-utils-Commandline_408/metadata.json b/Java/maven-shared-utils-Commandline_408/metadata.json new file mode 100644 index 000000000..44cc700b7 --- /dev/null +++ b/Java/maven-shared-utils-Commandline_408/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Commandline_408", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/Commandline.java", + "line": 410, + "npe_method": "execute", + "deref_field": "workingDir", + "npe_class": "Commandline", + "repo": "maven-shared-utils", + "bug_id": "Commandline_408" + } +} diff --git a/Java/maven-shared-utils-Commandline_408/npe.json b/Java/maven-shared-utils-Commandline_408/npe.json new file mode 100644 index 000000000..cf89f04f6 --- /dev/null +++ b/Java/maven-shared-utils-Commandline_408/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/Commandline.java", + "line": 410, + "npe_method": "execute", + "deref_field": "workingDir", + "npe_class": "Commandline" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-DirectoryScanner_403/Dockerfile b/Java/maven-shared-utils-DirectoryScanner_403/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_403/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-DirectoryScanner_403/buggy.java b/Java/maven-shared-utils-DirectoryScanner_403/buggy.java new file mode 100644 index 000000000..47edb5c7b --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_403/buggy.java @@ -0,0 +1,921 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Class for scanning a directory for files/directories which match certain criteria. + *

          + * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which + * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude + * files based on their filename. + *

          + * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is + * matched against a set of selectors, including special support for matching against filenames with include and and + * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file + * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will + * be placed in the list of files/directories found. + *

          + * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no + * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors + * are supplied, none are applied. + *

          + * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment + * is the name of a directory or file, which is bounded by File.separator ('/' under UNIX, '\' under + * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same + * is done for the pattern against which should be matched. + *

          + * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in + * the pattern, it matches zero or more path segments of the name. + *

          + * There is a special case regarding the use of File.separators at the beginning of the pattern and the + * string to match:
          + * When a pattern starts with a File.separator, the string to match must also start with a + * File.separator. When a pattern does not start with a File.separator, the string to match + * may not start with a File.separator. When one of these rules is not obeyed, the string will not match. + *

          + * When a name path segment is matched against a pattern path segment, the following special characters can be used:
          + * '*' matches zero or more characters
          + * '?' matches one character. + *

          + * Examples: + *

          + * "**\*.class" matches all .class files/dirs in a directory tree. + *

          + * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a + * directory called test. + *

          + * "**" matches everything in a directory tree. + *

          + * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test + * (e.g. "abc\test\def\ghi\XYZ123"). + *

          + * Case sensitivity may be turned off if necessary. By default, it is turned on. + *

          + * Example of usage: + *

          + *

          + * String[] includes = { "**\\*.class" };
          + * String[] excludes = { "modules\\*\\**" };
          + * ds.setIncludes( includes );
          + * ds.setExcludes( excludes );
          + * ds.setBasedir( new File( "test" ) );
          + * ds.setCaseSensitive( true );
          + * ds.scan();
          + *
          + * System.out.println( "FILES:" );
          + * String[] files = ds.getIncludedFiles();
          + * for ( int i = 0; i < files.length; i++ )
          + * {
          + *     System.out.println( files[i] );
          + * }
          + * 
          + *

          + * This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a + * directory called "modules" + *

          + * This class must not be used from multiple Threads concurrently! + * + * @author Arnout J. Kuiper ajkuiper@wxs.nl + * @author Magesh Umasankar + * @author Bruce Atherton + * @author Antoine Levy-Lambert + * @deprecated use {@code java.nio.file.DirectoryStream} and related classes + */ +@Deprecated +public class DirectoryScanner +{ + /** + * Patterns which should be excluded by default. + * + * @see #addDefaultExcludes() + */ + public static final String[] DEFAULTEXCLUDES = { + // Miscellaneous typical temporary files + "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", + + // CVS + "**/CVS", "**/CVS/**", "**/.cvsignore", + + // Subversion + "**/.svn", "**/.svn/**", + + // Arch + "**/.arch-ids", "**/.arch-ids/**", + + // Bazaar + "**/.bzr", "**/.bzr/**", + + // SurroundSCM + "**/.MySCMServerInfo", + + // Mac + "**/.DS_Store", + + // Serena Dimensions Version 10 + "**/.metadata", "**/.metadata/**", + + // Mercurial + "**/.hg", "**/.hg/**", + + // git + "**/.git", "**/.git/**", + + // BitKeeper + "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**", + + // darcs + "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" }; + + /** + * The base directory to be scanned. + */ + private File basedir; + + /** + * The patterns for the files to be included. + */ + private String[] includes; + + /** + * The patterns for the files to be excluded. + */ + private String[] excludes; + + private MatchPatterns excludesPatterns; + + private MatchPatterns includesPatterns; + + + /** + * The files which matched at least one include and no excludes and were selected. + */ + private List filesIncluded; + + /** + * The files which did not match any includes or selectors. + */ + private List filesNotIncluded; + + /** + * The files which matched at least one include and at least one exclude. + */ + private List filesExcluded; + + /** + * The directories which matched at least one include and no excludes and were selected. + */ + private List dirsIncluded; + + /** + * The directories which were found and did not match any includes. + */ + private List dirsNotIncluded; + + /** + * The directories which matched at least one include and at least one exclude. + */ + private List dirsExcluded; + + /** + * Whether or not our results were built by a slow scan. + */ + private boolean haveSlowResults = false; + + /** + * Whether or not the file system should be treated as a case sensitive one. + */ + private boolean isCaseSensitive = true; + + /** + * Whether or not symbolic links should be followed. + * + * + */ + private boolean followSymlinks = true; + + + /** + * A {@link ScanConductor} an control the scanning process. + */ + private ScanConductor scanConductor = null; + + /** + * The last ScanAction. We need to store this in the instance as the scan() method doesn't return + */ + private ScanConductor.ScanAction scanAction = null; + + /** + * Sole constructor. + */ + public DirectoryScanner() + { + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' + * characters are replaced by File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. Must not be null. + */ + public void setBasedir( final String basedir ) + { + setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) ); + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. + * + * @param basedir The base directory for scanning. Should not be null. + */ + public void setBasedir( @Nonnull final File basedir ) + { + this.basedir = basedir; + } + + /** + * Returns the base directory to be scanned. This is the directory which is scanned recursively. + * + * @return the base directory to be scanned + */ + public File getBasedir() + { + return basedir; + } + + /** + * Sets whether or not the file system should be regarded as case sensitive. + * + * @param isCaseSensitiveParameter whether or not the file system should be regarded as a case sensitive one + */ + public void setCaseSensitive( final boolean isCaseSensitiveParameter ) + { + this.isCaseSensitive = isCaseSensitiveParameter; + } + + /** + * Sets whether or not symbolic links should be followed. + * + * @param followSymlinks whether or not symbolic links should be followed + */ + public void setFollowSymlinks( final boolean followSymlinks ) + { + this.followSymlinks = followSymlinks; + } + + /** + * Sets the list of include patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param includes A list of include patterns. May be null, indicating that all files should be + * included. If a non-null list is given, all elements must be non-null. + */ + public void setIncludes( final String... includes ) + { + if ( includes == null ) + { + this.includes = null; + } + else + { + this.includes = new String[includes.length]; + for ( int i = 0; i < includes.length; i++ ) + { + String pattern; + pattern = includes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.includes[i] = pattern; + } + } + } + + /** + * Sets the list of exclude patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes A list of exclude patterns. May be null, indicating that no files should be + * excluded. If a non-null list is given, all elements must be non-null. + */ + public void setExcludes( final String... excludes ) + { + if ( excludes == null ) + { + this.excludes = null; + } + else + { + this.excludes = new String[excludes.length]; + for ( int i = 0; i < excludes.length; i++ ) + { + String pattern; + pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.excludes[i] = pattern; + } + } + } + + /** + * @param scanConductor {@link #scanConductor} + */ + public void setScanConductor( final ScanConductor scanConductor ) + { + this.scanConductor = scanConductor; + } + + /** + * Scans the base directory for files which match at least one include pattern and don't match any exclude patterns. + * If there are selectors then the files must pass muster there, as well. + * + * @throws IllegalStateException if the base directory was set incorrectly (i.e. if it is null, + * doesn't exist, or isn't a directory). + */ +/** + * Scans the base directory for files which match at least one include pattern and don't match any exclude patterns. + * If there are selectors then the files must pass muster there, as well. + * + * @throws IllegalStateException + * if the base directory was set incorrectly (i.e. if it is null, + * doesn't exist, or isn't a directory). + */ +public void scan() throws java.lang.IllegalStateException { + if (basedir == null) { + throw new java.lang.IllegalStateException("No basedir set"); + } + if (!basedir.exists()) { + throw new java.lang.IllegalStateException(("basedir " + basedir) + " does not exist"); + } + if (!basedir.isDirectory()) { + throw new java.lang.IllegalStateException(("basedir " + basedir) + " is not a directory"); + } + setupDefaultFilters(); + setupMatchPatterns(); + filesIncluded = new java.util.ArrayList(); + filesNotIncluded = new java.util.ArrayList(); + filesExcluded = new java.util.ArrayList(); + dirsIncluded = new java.util.ArrayList(); + dirsNotIncluded = new java.util.ArrayList(); + dirsExcluded = new java.util.ArrayList(); + scanAction = org.apache.maven.shared.utils.io.ScanConductor.ScanAction.CONTINUE; + if (isIncluded("")) { + if (!isExcluded("")) { + { + scanAction = /* NPEX_NULL_EXP */ + scanConductor.visitDirectory("", basedir); + if ((org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction) || org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT_DIRECTORY.equals(scanAction)) || org.apache.maven.shared.utils.io.ScanConductor.ScanAction.NO_RECURSE.equals(scanAction)) { + return; + } + } + dirsIncluded.add(""); + } else { + dirsExcluded.add(""); + } + } else { + dirsNotIncluded.add(""); + } + scandir(basedir, "", true); +} + + /** + * Determine the file differences between the currently included files and + * a previously captured list of files. + * This method will not look for a changed in content but sole in the + * list of files given. + *

          + * The method will compare the given array of file Strings with the result + * of the last directory scan. It will execute a {@link #scan()} if no + * result of a previous scan could be found. + *

          + * The result of the diff can be queried by the methods + * {@link DirectoryScanResult#getFilesAdded()} and {@link DirectoryScanResult#getFilesRemoved()} + * + * @param oldFiles the list of previously captured files names. + * @return the result of the directory scan. + */ + public DirectoryScanResult diffIncludedFiles( String... oldFiles ) + { + if ( filesIncluded == null ) + { + // perform a scan if the directory didn't got scanned yet + scan(); + } + + return diffFiles( oldFiles, filesIncluded.toArray( new String[filesIncluded.size()] ) ); + } + + /** + * @param oldFiles array of old files + * @param newFiles array of new files + * @return calculated difference + */ + public static DirectoryScanResult diffFiles( @Nullable String[] oldFiles, @Nullable String[] newFiles ) + { + Set oldFileSet = arrayAsHashSet( oldFiles ); + Set newFileSet = arrayAsHashSet( newFiles ); + + List added = new ArrayList(); + List removed = new ArrayList(); + + for ( String oldFile : oldFileSet ) + { + if ( !newFileSet.contains( oldFile ) ) + { + removed.add( oldFile ); + } + } + + for ( String newFile : newFileSet ) + { + if ( !oldFileSet.contains( newFile ) ) + { + added.add( newFile ); + } + } + + String[] filesAdded = added.toArray( new String[added.size()] ); + String[] filesRemoved = removed.toArray( new String[removed.size()] ); + + return new DirectoryScanResult( filesAdded, filesRemoved ); + } + + + /** + * Take an array of type T and convert it into a HashSet of type T. + * If null or an empty array gets passed, an empty Set will be returned. + * + * @param array The array + * @return the filled HashSet of type T + */ + private static Set arrayAsHashSet( @Nullable T[] array ) + { + if ( array == null || array.length == 0 ) + { + return Collections.emptySet(); + } + + Set set = new HashSet( array.length ); + Collections.addAll( set, array ); + + return set; + } + + /** + * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories, + * whereas a fast scan will only have full results for included files, as it ignores directories which can't + * possibly hold any included files/directories. + *

          + * Returns immediately if a slow scan has already been completed. + */ + void slowScan() + { + if ( haveSlowResults ) + { + return; + } + + final String[] excl = dirsExcluded.toArray( new String[dirsExcluded.size()] ); + + final String[] notIncl = dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + + for ( String anExcl : excl ) + { + if ( !couldHoldIncluded( anExcl ) ) + { + scandir( new File( basedir, anExcl ), anExcl + File.separator, false ); + } + } + + for ( String aNotIncl : notIncl ) + { + if ( !couldHoldIncluded( aNotIncl ) ) + { + scandir( new File( basedir, aNotIncl ), aNotIncl + File.separator, false ); + } + } + + haveSlowResults = true; + } + + /** + * Scans the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast Whether or not this call is part of a fast scan. + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ + void scandir( @Nonnull final File dir, @Nonnull final String vpath, final boolean fast ) + { + String[] newfiles = dir.list(); + + if ( newfiles == null ) + { + /* + * two reasons are mentioned in the API docs for File.list (1) dir is not a directory. This is impossible as + * we wouldn't get here in this case. (2) an IO error occurred (why doesn't it throw an exception then???) + */ + + /* + * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... this is affecting the + * assembly plugin, fwiw. I will initialize the newfiles array as zero-length for now. NOTE: I can't find + * the problematic code, as it appears to come from a native method in UnixFileSystem... + */ + newfiles = new String[0]; + + // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() ); + } + + if ( !followSymlinks ) + { + newfiles = doNotFollowSymbolicLinks( dir, vpath, newfiles ); + } + + for ( final String newfile : newfiles ) + { + final String name = vpath + newfile; + final File file = new File( dir, newfile ); + if ( file.isDirectory() ) + { + if ( isIncluded( name ) ) + { + if ( !isExcluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( name, file ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + } + + if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + dirsIncluded.add( name ); + if ( fast ) + { + scandir( file, name + File.separator, fast ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + } + } + scanAction = null; + + } + else + { + dirsExcluded.add( name ); + if ( fast && couldHoldIncluded( name ) ) + { + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + scanAction = null; + } + } + } + else + { + if ( fast && couldHoldIncluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( name, file ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + } + if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + dirsNotIncluded.add( name ); + + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + } + scanAction = null; + } + } + if ( !fast ) + { + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + scanAction = null; + } + } + else if ( file.isFile() ) + { + if ( isIncluded( name ) ) + { + if ( !isExcluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitFile( name, file ); + } + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + + filesIncluded.add( name ); + } + else + { + filesExcluded.add( name ); + } + } + else + { + filesNotIncluded.add( name ); + } + } + } + } + + private String[] doNotFollowSymbolicLinks( final File dir, final String vpath, String[] newfiles ) + { + final List noLinks = new ArrayList(); + for ( final String newfile : newfiles ) + { + try + { + if ( isSymbolicLink( dir, newfile ) ) + { + final String name = vpath + newfile; + final File file = new File( dir, newfile ); + if ( file.isDirectory() ) + { + dirsExcluded.add( name ); + } + else + { + filesExcluded.add( name ); + } + } + else + { + noLinks.add( newfile ); + } + } + catch ( final IOException ioe ) + { + final String msg = + "IOException caught while checking " + "for links, couldn't get cannonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println( msg ); + noLinks.add( newfile ); + } + } + newfiles = noLinks.toArray( new String[noLinks.size()] ); + return newfiles; + } + + /** + * Tests whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + boolean isIncluded( final String name ) + { + return includesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches the start of at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + boolean couldHoldIncluded( @Nonnull final String name ) + { + return includesPatterns.matchesPatternStart( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + boolean isExcluded( @Nonnull final String name ) + { + return excludesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method does not work correctly on Windows. + * @return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to files. + */ + @Deprecated + public String[] getIncludedFiles() + { + if ( filesIncluded == null ) + { + return new String[0]; + } + return filesIncluded.toArray( new String[filesIncluded.size()] ); + } + + /** + * Returns the names of the files which matched none of the include patterns. The names are relative to the base + * directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the files which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedFiles() + { + slowScan(); + return filesNotIncluded.toArray( new String[filesNotIncluded.size()] ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and at least one of the exclude + * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not + * already been completed. + * + * @return the names of the files which matched at least one of the include patterns and at at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedFiles() + { + slowScan(); + return filesExcluded.toArray( new String[filesExcluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method is buggy. Do not depend on it. + * @return the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to directories. + */ + @Deprecated + public String[] getIncludedDirectories() + { + return dirsIncluded.toArray( new String[dirsIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched none of the include patterns. The names are relative to the + * base directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the directories which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedDirectories() + { + slowScan(); + return dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has + * not already been completed. + * + * @return the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedDirectories() + { + slowScan(); + return dirsExcluded.toArray( new String[dirsExcluded.size()] ); + } + + /** + * Adds default exclusions to the current exclusions set. + */ + public void addDefaultExcludes() + { + final int excludesLength = excludes == null ? 0 : excludes.length; + String[] newExcludes; + newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length]; + if ( excludesLength > 0 ) + { + System.arraycopy( excludes, 0, newExcludes, 0, excludesLength ); + } + for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ ) + { + newExcludes[i + excludesLength] = + DEFAULTEXCLUDES[i].replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + excludes = newExcludes; + } + + /** + * Checks whether a given file is a symbolic link. + *

          + * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are identical + * - this may lead to false positives on some platforms. + *

          + * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + */ + boolean isSymbolicLink( final File parent, final String name ) + throws IOException + { + return Files.isSymbolicLink( parent.toPath() ); + } + + private void setupDefaultFilters() + { + if ( includes == null ) + { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + if ( excludes == null ) + { + excludes = new String[0]; + } + } + + + private void setupMatchPatterns() + { + includesPatterns = MatchPatterns.from( includes ); + excludesPatterns = MatchPatterns.from( excludes ); + } + +} diff --git a/Java/maven-shared-utils-DirectoryScanner_403/metadata.json b/Java/maven-shared-utils-DirectoryScanner_403/metadata.json new file mode 100644 index 000000000..ca89a903f --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_403/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-DirectoryScanner_403", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java", + "line": 403, + "npe_method": "scan", + "deref_field": "scanConductor", + "npe_class": "DirectoryScanner", + "repo": "maven-shared-utils", + "bug_id": "DirectoryScanner_403" + } +} diff --git a/Java/maven-shared-utils-DirectoryScanner_403/npe.json b/Java/maven-shared-utils-DirectoryScanner_403/npe.json new file mode 100644 index 000000000..579593dd2 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_403/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java", + "line": 403, + "npe_method": "scan", + "deref_field": "scanConductor", + "npe_class": "DirectoryScanner" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-DirectoryScanner_602/Dockerfile b/Java/maven-shared-utils-DirectoryScanner_602/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_602/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-DirectoryScanner_602/buggy.java b/Java/maven-shared-utils-DirectoryScanner_602/buggy.java new file mode 100644 index 000000000..ce052d54e --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_602/buggy.java @@ -0,0 +1,900 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Class for scanning a directory for files/directories which match certain criteria. + *

          + * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which + * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude + * files based on their filename. + *

          + * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is + * matched against a set of selectors, including special support for matching against filenames with include and and + * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file + * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will + * be placed in the list of files/directories found. + *

          + * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no + * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors + * are supplied, none are applied. + *

          + * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment + * is the name of a directory or file, which is bounded by File.separator ('/' under UNIX, '\' under + * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same + * is done for the pattern against which should be matched. + *

          + * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in + * the pattern, it matches zero or more path segments of the name. + *

          + * There is a special case regarding the use of File.separators at the beginning of the pattern and the + * string to match:
          + * When a pattern starts with a File.separator, the string to match must also start with a + * File.separator. When a pattern does not start with a File.separator, the string to match + * may not start with a File.separator. When one of these rules is not obeyed, the string will not match. + *

          + * When a name path segment is matched against a pattern path segment, the following special characters can be used:
          + * '*' matches zero or more characters
          + * '?' matches one character. + *

          + * Examples: + *

          + * "**\*.class" matches all .class files/dirs in a directory tree. + *

          + * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a + * directory called test. + *

          + * "**" matches everything in a directory tree. + *

          + * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test + * (e.g. "abc\test\def\ghi\XYZ123"). + *

          + * Case sensitivity may be turned off if necessary. By default, it is turned on. + *

          + * Example of usage: + *

          + *

          + * String[] includes = { "**\\*.class" };
          + * String[] excludes = { "modules\\*\\**" };
          + * ds.setIncludes( includes );
          + * ds.setExcludes( excludes );
          + * ds.setBasedir( new File( "test" ) );
          + * ds.setCaseSensitive( true );
          + * ds.scan();
          + *
          + * System.out.println( "FILES:" );
          + * String[] files = ds.getIncludedFiles();
          + * for ( int i = 0; i < files.length; i++ )
          + * {
          + *     System.out.println( files[i] );
          + * }
          + * 
          + *

          + * This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a + * directory called "modules" + *

          + * This class must not be used from multiple Threads concurrently! + * + * @author Arnout J. Kuiper ajkuiper@wxs.nl + * @author Magesh Umasankar + * @author Bruce Atherton + * @author Antoine Levy-Lambert + * @deprecated use {@code java.nio.file.DirectoryStream} and related classes + */ +@Deprecated +public class DirectoryScanner +{ + /** + * Patterns which should be excluded by default. + * + * @see #addDefaultExcludes() + */ + public static final String[] DEFAULTEXCLUDES = { + // Miscellaneous typical temporary files + "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", + + // CVS + "**/CVS", "**/CVS/**", "**/.cvsignore", + + // Subversion + "**/.svn", "**/.svn/**", + + // Arch + "**/.arch-ids", "**/.arch-ids/**", + + // Bazaar + "**/.bzr", "**/.bzr/**", + + // SurroundSCM + "**/.MySCMServerInfo", + + // Mac + "**/.DS_Store", + + // Serena Dimensions Version 10 + "**/.metadata", "**/.metadata/**", + + // Mercurial + "**/.hg", "**/.hg/**", + + // git + "**/.git", "**/.git/**", + + // BitKeeper + "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**", + + // darcs + "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" }; + + /** + * The base directory to be scanned. + */ + private File basedir; + + /** + * The patterns for the files to be included. + */ + private String[] includes; + + /** + * The patterns for the files to be excluded. + */ + private String[] excludes; + + private MatchPatterns excludesPatterns; + + private MatchPatterns includesPatterns; + + + /** + * The files which matched at least one include and no excludes and were selected. + */ + private List filesIncluded; + + /** + * The files which did not match any includes or selectors. + */ + private List filesNotIncluded; + + /** + * The files which matched at least one include and at least one exclude. + */ + private List filesExcluded; + + /** + * The directories which matched at least one include and no excludes and were selected. + */ + private List dirsIncluded; + + /** + * The directories which were found and did not match any includes. + */ + private List dirsNotIncluded; + + /** + * The directories which matched at least one include and at least one exclude. + */ + private List dirsExcluded; + + /** + * Whether or not our results were built by a slow scan. + */ + private boolean haveSlowResults = false; + + /** + * Whether or not the file system should be treated as a case sensitive one. + */ + private boolean isCaseSensitive = true; + + /** + * Whether or not symbolic links should be followed. + * + * + */ + private boolean followSymlinks = true; + + + /** + * A {@link ScanConductor} an control the scanning process. + */ + private ScanConductor scanConductor = null; + + /** + * The last ScanAction. We need to store this in the instance as the scan() method doesn't return + */ + private ScanConductor.ScanAction scanAction = null; + + /** + * Sole constructor. + */ + public DirectoryScanner() + { + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' + * characters are replaced by File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. Must not be null. + */ + public void setBasedir( final String basedir ) + { + setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) ); + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. + * + * @param basedir The base directory for scanning. Should not be null. + */ + public void setBasedir( @Nonnull final File basedir ) + { + this.basedir = basedir; + } + + /** + * Returns the base directory to be scanned. This is the directory which is scanned recursively. + * + * @return the base directory to be scanned + */ + public File getBasedir() + { + return basedir; + } + + /** + * Sets whether or not the file system should be regarded as case sensitive. + * + * @param isCaseSensitiveParameter whether or not the file system should be regarded as a case sensitive one + */ + public void setCaseSensitive( final boolean isCaseSensitiveParameter ) + { + this.isCaseSensitive = isCaseSensitiveParameter; + } + + /** + * Sets whether or not symbolic links should be followed. + * + * @param followSymlinks whether or not symbolic links should be followed + */ + public void setFollowSymlinks( final boolean followSymlinks ) + { + this.followSymlinks = followSymlinks; + } + + /** + * Sets the list of include patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param includes A list of include patterns. May be null, indicating that all files should be + * included. If a non-null list is given, all elements must be non-null. + */ + public void setIncludes( final String... includes ) + { + if ( includes == null ) + { + this.includes = null; + } + else + { + this.includes = new String[includes.length]; + for ( int i = 0; i < includes.length; i++ ) + { + String pattern; + pattern = includes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.includes[i] = pattern; + } + } + } + + /** + * Sets the list of exclude patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes A list of exclude patterns. May be null, indicating that no files should be + * excluded. If a non-null list is given, all elements must be non-null. + */ + public void setExcludes( final String... excludes ) + { + if ( excludes == null ) + { + this.excludes = null; + } + else + { + this.excludes = new String[excludes.length]; + for ( int i = 0; i < excludes.length; i++ ) + { + String pattern; + pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.excludes[i] = pattern; + } + } + } + + /** + * @param scanConductor {@link #scanConductor} + */ + public void setScanConductor( final ScanConductor scanConductor ) + { + this.scanConductor = scanConductor; + } + + /** + * Scans the base directory for files which match at least one include pattern and don't match any exclude patterns. + * If there are selectors then the files must pass muster there, as well. + * + * @throws IllegalStateException if the base directory was set incorrectly (i.e. if it is null, + * doesn't exist, or isn't a directory). + */ + public void scan() + throws IllegalStateException + { + if ( basedir == null ) + { + throw new IllegalStateException( "No basedir set" ); + } + if ( !basedir.exists() ) + { + throw new IllegalStateException( "basedir " + basedir + " does not exist" ); + } + if ( !basedir.isDirectory() ) + { + throw new IllegalStateException( "basedir " + basedir + " is not a directory" ); + } + + setupDefaultFilters(); + setupMatchPatterns(); + + filesIncluded = new ArrayList(); + filesNotIncluded = new ArrayList(); + filesExcluded = new ArrayList(); + dirsIncluded = new ArrayList(); + dirsNotIncluded = new ArrayList(); + dirsExcluded = new ArrayList(); + scanAction = ScanConductor.ScanAction.CONTINUE; + + if ( isIncluded( "" ) ) + { + if ( !isExcluded( "" ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( "", basedir ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) + || ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + return; + } + } + + dirsIncluded.add( "" ); + } + else + { + dirsExcluded.add( "" ); + } + } + else + { + dirsNotIncluded.add( "" ); + } + scandir( basedir, "", true ); + } + + /** + * Determine the file differences between the currently included files and + * a previously captured list of files. + * This method will not look for a changed in content but sole in the + * list of files given. + *

          + * The method will compare the given array of file Strings with the result + * of the last directory scan. It will execute a {@link #scan()} if no + * result of a previous scan could be found. + *

          + * The result of the diff can be queried by the methods + * {@link DirectoryScanResult#getFilesAdded()} and {@link DirectoryScanResult#getFilesRemoved()} + * + * @param oldFiles the list of previously captured files names. + * @return the result of the directory scan. + */ + public DirectoryScanResult diffIncludedFiles( String... oldFiles ) + { + if ( filesIncluded == null ) + { + // perform a scan if the directory didn't got scanned yet + scan(); + } + + return diffFiles( oldFiles, filesIncluded.toArray( new String[filesIncluded.size()] ) ); + } + + /** + * @param oldFiles array of old files + * @param newFiles array of new files + * @return calculated difference + */ + public static DirectoryScanResult diffFiles( @Nullable String[] oldFiles, @Nullable String[] newFiles ) + { + Set oldFileSet = arrayAsHashSet( oldFiles ); + Set newFileSet = arrayAsHashSet( newFiles ); + + List added = new ArrayList(); + List removed = new ArrayList(); + + for ( String oldFile : oldFileSet ) + { + if ( !newFileSet.contains( oldFile ) ) + { + removed.add( oldFile ); + } + } + + for ( String newFile : newFileSet ) + { + if ( !oldFileSet.contains( newFile ) ) + { + added.add( newFile ); + } + } + + String[] filesAdded = added.toArray( new String[added.size()] ); + String[] filesRemoved = removed.toArray( new String[removed.size()] ); + + return new DirectoryScanResult( filesAdded, filesRemoved ); + } + + + /** + * Take an array of type T and convert it into a HashSet of type T. + * If null or an empty array gets passed, an empty Set will be returned. + * + * @param array The array + * @return the filled HashSet of type T + */ + private static Set arrayAsHashSet( @Nullable T[] array ) + { + if ( array == null || array.length == 0 ) + { + return Collections.emptySet(); + } + + Set set = new HashSet( array.length ); + Collections.addAll( set, array ); + + return set; + } + + /** + * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories, + * whereas a fast scan will only have full results for included files, as it ignores directories which can't + * possibly hold any included files/directories. + *

          + * Returns immediately if a slow scan has already been completed. + */ + void slowScan() + { + if ( haveSlowResults ) + { + return; + } + + final String[] excl = dirsExcluded.toArray( new String[dirsExcluded.size()] ); + + final String[] notIncl = dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + + for ( String anExcl : excl ) + { + if ( !couldHoldIncluded( anExcl ) ) + { + scandir( new File( basedir, anExcl ), anExcl + File.separator, false ); + } + } + + for ( String aNotIncl : notIncl ) + { + if ( !couldHoldIncluded( aNotIncl ) ) + { + scandir( new File( basedir, aNotIncl ), aNotIncl + File.separator, false ); + } + } + + haveSlowResults = true; + } + + /** + * Scans the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast Whether or not this call is part of a fast scan. + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ +/** + * Scans the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir + * The directory to scan. Must not be null. + * @param vpath + * The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast + * Whether or not this call is part of a fast scan. + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ +void scandir(@javax.annotation.Nonnull +final java.io.File dir, @javax.annotation.Nonnull +final java.lang.String vpath, final boolean fast) { + java.lang.String[] newfiles = dir.list(); + if (newfiles == null) { + /* two reasons are mentioned in the API docs for File.list (1) dir is not a directory. This is impossible as + we wouldn't get here in this case. (2) an IO error occurred (why doesn't it throw an exception then???) + */ + /* [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... this is affecting the + assembly plugin, fwiw. I will initialize the newfiles array as zero-length for now. NOTE: I can't find + the problematic code, as it appears to come from a native method in UnixFileSystem... + */ + newfiles = new java.lang.String[0]; + // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() ); + } + if (!followSymlinks) { + newfiles = doNotFollowSymbolicLinks(dir, vpath, newfiles); + } + for (final java.lang.String newfile : newfiles) { + final java.lang.String name = vpath + newfile; + final java.io.File file = new java.io.File(dir, newfile); + if (file.isDirectory()) { + if (isIncluded(name)) { + if (!isExcluded(name)) { + { + scanAction = /* NPEX_NULL_EXP */ + scanConductor.visitDirectory(name, file); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction) || org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT_DIRECTORY.equals(scanAction)) { + return; + } + } + if (!org.apache.maven.shared.utils.io.ScanConductor.ScanAction.NO_RECURSE.equals(scanAction)) { + dirsIncluded.add(name); + if (fast) { + scandir(file, name + java.io.File.separator, fast); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction)) { + return; + } + } + } + scanAction = null; + } else { + dirsExcluded.add(name); + if (fast && couldHoldIncluded(name)) { + scandir(file, name + java.io.File.separator, fast); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction)) { + return; + } + scanAction = null; + } + } + } else if (fast && couldHoldIncluded(name)) { + if (scanConductor != null) { + scanAction = scanConductor.visitDirectory(name, file); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction) || org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT_DIRECTORY.equals(scanAction)) { + return; + } + } + if (!org.apache.maven.shared.utils.io.ScanConductor.ScanAction.NO_RECURSE.equals(scanAction)) { + dirsNotIncluded.add(name); + scandir(file, name + java.io.File.separator, fast); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction)) { + return; + } + } + scanAction = null; + } + if (!fast) { + scandir(file, name + java.io.File.separator, fast); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction)) { + return; + } + scanAction = null; + } + } else if (file.isFile()) { + if (isIncluded(name)) { + if (!isExcluded(name)) { + if (scanConductor != null) { + scanAction = scanConductor.visitFile(name, file); + } + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction) || org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT_DIRECTORY.equals(scanAction)) { + return; + } + filesIncluded.add(name); + } else { + filesExcluded.add(name); + } + } else { + filesNotIncluded.add(name); + } + } + } +} + + private String[] doNotFollowSymbolicLinks( final File dir, final String vpath, String[] newfiles ) + { + final List noLinks = new ArrayList(); + for ( final String newfile : newfiles ) + { + try + { + if ( isSymbolicLink( dir, newfile ) ) + { + final String name = vpath + newfile; + final File file = new File( dir, newfile ); + if ( file.isDirectory() ) + { + dirsExcluded.add( name ); + } + else + { + filesExcluded.add( name ); + } + } + else + { + noLinks.add( newfile ); + } + } + catch ( final IOException ioe ) + { + final String msg = + "IOException caught while checking " + "for links, couldn't get cannonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println( msg ); + noLinks.add( newfile ); + } + } + newfiles = noLinks.toArray( new String[noLinks.size()] ); + return newfiles; + } + + /** + * Tests whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + boolean isIncluded( final String name ) + { + return includesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches the start of at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + boolean couldHoldIncluded( @Nonnull final String name ) + { + return includesPatterns.matchesPatternStart( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + boolean isExcluded( @Nonnull final String name ) + { + return excludesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method does not work correctly on Windows. + * @return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to files. + */ + @Deprecated + public String[] getIncludedFiles() + { + if ( filesIncluded == null ) + { + return new String[0]; + } + return filesIncluded.toArray( new String[filesIncluded.size()] ); + } + + /** + * Returns the names of the files which matched none of the include patterns. The names are relative to the base + * directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the files which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedFiles() + { + slowScan(); + return filesNotIncluded.toArray( new String[filesNotIncluded.size()] ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and at least one of the exclude + * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not + * already been completed. + * + * @return the names of the files which matched at least one of the include patterns and at at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedFiles() + { + slowScan(); + return filesExcluded.toArray( new String[filesExcluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method is buggy. Do not depend on it. + * @return the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to directories. + */ + @Deprecated + public String[] getIncludedDirectories() + { + return dirsIncluded.toArray( new String[dirsIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched none of the include patterns. The names are relative to the + * base directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the directories which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedDirectories() + { + slowScan(); + return dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has + * not already been completed. + * + * @return the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedDirectories() + { + slowScan(); + return dirsExcluded.toArray( new String[dirsExcluded.size()] ); + } + + /** + * Adds default exclusions to the current exclusions set. + */ + public void addDefaultExcludes() + { + final int excludesLength = excludes == null ? 0 : excludes.length; + String[] newExcludes; + newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length]; + if ( excludesLength > 0 ) + { + System.arraycopy( excludes, 0, newExcludes, 0, excludesLength ); + } + for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ ) + { + newExcludes[i + excludesLength] = + DEFAULTEXCLUDES[i].replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + excludes = newExcludes; + } + + /** + * Checks whether a given file is a symbolic link. + *

          + * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are identical + * - this may lead to false positives on some platforms. + *

          + * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + */ + boolean isSymbolicLink( final File parent, final String name ) + throws IOException + { + return Files.isSymbolicLink( parent.toPath() ); + } + + private void setupDefaultFilters() + { + if ( includes == null ) + { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + if ( excludes == null ) + { + excludes = new String[0]; + } + } + + + private void setupMatchPatterns() + { + includesPatterns = MatchPatterns.from( includes ); + excludesPatterns = MatchPatterns.from( excludes ); + } + +} diff --git a/Java/maven-shared-utils-DirectoryScanner_602/metadata.json b/Java/maven-shared-utils-DirectoryScanner_602/metadata.json new file mode 100644 index 000000000..d9fb2b177 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_602/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-DirectoryScanner_602", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java", + "line": 612, + "npe_method": "scandir", + "deref_field": "scanConductor", + "npe_class": "DirectoryScanner", + "repo": "maven-shared-utils", + "bug_id": "DirectoryScanner_602" + } +} diff --git a/Java/maven-shared-utils-DirectoryScanner_602/npe.json b/Java/maven-shared-utils-DirectoryScanner_602/npe.json new file mode 100644 index 000000000..6be0239ce --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_602/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java", + "line": 612, + "npe_method": "scandir", + "deref_field": "scanConductor", + "npe_class": "DirectoryScanner" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-DirectoryScanner_686/Dockerfile b/Java/maven-shared-utils-DirectoryScanner_686/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_686/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-DirectoryScanner_686/buggy.java b/Java/maven-shared-utils-DirectoryScanner_686/buggy.java new file mode 100644 index 000000000..ae34e06f9 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_686/buggy.java @@ -0,0 +1,900 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Class for scanning a directory for files/directories which match certain criteria. + *

          + * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which + * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude + * files based on their filename. + *

          + * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is + * matched against a set of selectors, including special support for matching against filenames with include and and + * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file + * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will + * be placed in the list of files/directories found. + *

          + * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no + * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors + * are supplied, none are applied. + *

          + * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment + * is the name of a directory or file, which is bounded by File.separator ('/' under UNIX, '\' under + * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same + * is done for the pattern against which should be matched. + *

          + * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in + * the pattern, it matches zero or more path segments of the name. + *

          + * There is a special case regarding the use of File.separators at the beginning of the pattern and the + * string to match:
          + * When a pattern starts with a File.separator, the string to match must also start with a + * File.separator. When a pattern does not start with a File.separator, the string to match + * may not start with a File.separator. When one of these rules is not obeyed, the string will not match. + *

          + * When a name path segment is matched against a pattern path segment, the following special characters can be used:
          + * '*' matches zero or more characters
          + * '?' matches one character. + *

          + * Examples: + *

          + * "**\*.class" matches all .class files/dirs in a directory tree. + *

          + * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a + * directory called test. + *

          + * "**" matches everything in a directory tree. + *

          + * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test + * (e.g. "abc\test\def\ghi\XYZ123"). + *

          + * Case sensitivity may be turned off if necessary. By default, it is turned on. + *

          + * Example of usage: + *

          + *

          + * String[] includes = { "**\\*.class" };
          + * String[] excludes = { "modules\\*\\**" };
          + * ds.setIncludes( includes );
          + * ds.setExcludes( excludes );
          + * ds.setBasedir( new File( "test" ) );
          + * ds.setCaseSensitive( true );
          + * ds.scan();
          + *
          + * System.out.println( "FILES:" );
          + * String[] files = ds.getIncludedFiles();
          + * for ( int i = 0; i < files.length; i++ )
          + * {
          + *     System.out.println( files[i] );
          + * }
          + * 
          + *

          + * This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a + * directory called "modules" + *

          + * This class must not be used from multiple Threads concurrently! + * + * @author Arnout J. Kuiper ajkuiper@wxs.nl + * @author Magesh Umasankar + * @author Bruce Atherton + * @author Antoine Levy-Lambert + * @deprecated use {@code java.nio.file.DirectoryStream} and related classes + */ +@Deprecated +public class DirectoryScanner +{ + /** + * Patterns which should be excluded by default. + * + * @see #addDefaultExcludes() + */ + public static final String[] DEFAULTEXCLUDES = { + // Miscellaneous typical temporary files + "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", + + // CVS + "**/CVS", "**/CVS/**", "**/.cvsignore", + + // Subversion + "**/.svn", "**/.svn/**", + + // Arch + "**/.arch-ids", "**/.arch-ids/**", + + // Bazaar + "**/.bzr", "**/.bzr/**", + + // SurroundSCM + "**/.MySCMServerInfo", + + // Mac + "**/.DS_Store", + + // Serena Dimensions Version 10 + "**/.metadata", "**/.metadata/**", + + // Mercurial + "**/.hg", "**/.hg/**", + + // git + "**/.git", "**/.git/**", + + // BitKeeper + "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**", + + // darcs + "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" }; + + /** + * The base directory to be scanned. + */ + private File basedir; + + /** + * The patterns for the files to be included. + */ + private String[] includes; + + /** + * The patterns for the files to be excluded. + */ + private String[] excludes; + + private MatchPatterns excludesPatterns; + + private MatchPatterns includesPatterns; + + + /** + * The files which matched at least one include and no excludes and were selected. + */ + private List filesIncluded; + + /** + * The files which did not match any includes or selectors. + */ + private List filesNotIncluded; + + /** + * The files which matched at least one include and at least one exclude. + */ + private List filesExcluded; + + /** + * The directories which matched at least one include and no excludes and were selected. + */ + private List dirsIncluded; + + /** + * The directories which were found and did not match any includes. + */ + private List dirsNotIncluded; + + /** + * The directories which matched at least one include and at least one exclude. + */ + private List dirsExcluded; + + /** + * Whether or not our results were built by a slow scan. + */ + private boolean haveSlowResults = false; + + /** + * Whether or not the file system should be treated as a case sensitive one. + */ + private boolean isCaseSensitive = true; + + /** + * Whether or not symbolic links should be followed. + * + * + */ + private boolean followSymlinks = true; + + + /** + * A {@link ScanConductor} an control the scanning process. + */ + private ScanConductor scanConductor = null; + + /** + * The last ScanAction. We need to store this in the instance as the scan() method doesn't return + */ + private ScanConductor.ScanAction scanAction = null; + + /** + * Sole constructor. + */ + public DirectoryScanner() + { + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' + * characters are replaced by File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. Must not be null. + */ + public void setBasedir( final String basedir ) + { + setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) ); + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. + * + * @param basedir The base directory for scanning. Should not be null. + */ + public void setBasedir( @Nonnull final File basedir ) + { + this.basedir = basedir; + } + + /** + * Returns the base directory to be scanned. This is the directory which is scanned recursively. + * + * @return the base directory to be scanned + */ + public File getBasedir() + { + return basedir; + } + + /** + * Sets whether or not the file system should be regarded as case sensitive. + * + * @param isCaseSensitiveParameter whether or not the file system should be regarded as a case sensitive one + */ + public void setCaseSensitive( final boolean isCaseSensitiveParameter ) + { + this.isCaseSensitive = isCaseSensitiveParameter; + } + + /** + * Sets whether or not symbolic links should be followed. + * + * @param followSymlinks whether or not symbolic links should be followed + */ + public void setFollowSymlinks( final boolean followSymlinks ) + { + this.followSymlinks = followSymlinks; + } + + /** + * Sets the list of include patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param includes A list of include patterns. May be null, indicating that all files should be + * included. If a non-null list is given, all elements must be non-null. + */ + public void setIncludes( final String... includes ) + { + if ( includes == null ) + { + this.includes = null; + } + else + { + this.includes = new String[includes.length]; + for ( int i = 0; i < includes.length; i++ ) + { + String pattern; + pattern = includes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.includes[i] = pattern; + } + } + } + + /** + * Sets the list of exclude patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes A list of exclude patterns. May be null, indicating that no files should be + * excluded. If a non-null list is given, all elements must be non-null. + */ + public void setExcludes( final String... excludes ) + { + if ( excludes == null ) + { + this.excludes = null; + } + else + { + this.excludes = new String[excludes.length]; + for ( int i = 0; i < excludes.length; i++ ) + { + String pattern; + pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.excludes[i] = pattern; + } + } + } + + /** + * @param scanConductor {@link #scanConductor} + */ + public void setScanConductor( final ScanConductor scanConductor ) + { + this.scanConductor = scanConductor; + } + + /** + * Scans the base directory for files which match at least one include pattern and don't match any exclude patterns. + * If there are selectors then the files must pass muster there, as well. + * + * @throws IllegalStateException if the base directory was set incorrectly (i.e. if it is null, + * doesn't exist, or isn't a directory). + */ + public void scan() + throws IllegalStateException + { + if ( basedir == null ) + { + throw new IllegalStateException( "No basedir set" ); + } + if ( !basedir.exists() ) + { + throw new IllegalStateException( "basedir " + basedir + " does not exist" ); + } + if ( !basedir.isDirectory() ) + { + throw new IllegalStateException( "basedir " + basedir + " is not a directory" ); + } + + setupDefaultFilters(); + setupMatchPatterns(); + + filesIncluded = new ArrayList(); + filesNotIncluded = new ArrayList(); + filesExcluded = new ArrayList(); + dirsIncluded = new ArrayList(); + dirsNotIncluded = new ArrayList(); + dirsExcluded = new ArrayList(); + scanAction = ScanConductor.ScanAction.CONTINUE; + + if ( isIncluded( "" ) ) + { + if ( !isExcluded( "" ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( "", basedir ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) + || ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + return; + } + } + + dirsIncluded.add( "" ); + } + else + { + dirsExcluded.add( "" ); + } + } + else + { + dirsNotIncluded.add( "" ); + } + scandir( basedir, "", true ); + } + + /** + * Determine the file differences between the currently included files and + * a previously captured list of files. + * This method will not look for a changed in content but sole in the + * list of files given. + *

          + * The method will compare the given array of file Strings with the result + * of the last directory scan. It will execute a {@link #scan()} if no + * result of a previous scan could be found. + *

          + * The result of the diff can be queried by the methods + * {@link DirectoryScanResult#getFilesAdded()} and {@link DirectoryScanResult#getFilesRemoved()} + * + * @param oldFiles the list of previously captured files names. + * @return the result of the directory scan. + */ + public DirectoryScanResult diffIncludedFiles( String... oldFiles ) + { + if ( filesIncluded == null ) + { + // perform a scan if the directory didn't got scanned yet + scan(); + } + + return diffFiles( oldFiles, filesIncluded.toArray( new String[filesIncluded.size()] ) ); + } + + /** + * @param oldFiles array of old files + * @param newFiles array of new files + * @return calculated difference + */ + public static DirectoryScanResult diffFiles( @Nullable String[] oldFiles, @Nullable String[] newFiles ) + { + Set oldFileSet = arrayAsHashSet( oldFiles ); + Set newFileSet = arrayAsHashSet( newFiles ); + + List added = new ArrayList(); + List removed = new ArrayList(); + + for ( String oldFile : oldFileSet ) + { + if ( !newFileSet.contains( oldFile ) ) + { + removed.add( oldFile ); + } + } + + for ( String newFile : newFileSet ) + { + if ( !oldFileSet.contains( newFile ) ) + { + added.add( newFile ); + } + } + + String[] filesAdded = added.toArray( new String[added.size()] ); + String[] filesRemoved = removed.toArray( new String[removed.size()] ); + + return new DirectoryScanResult( filesAdded, filesRemoved ); + } + + + /** + * Take an array of type T and convert it into a HashSet of type T. + * If null or an empty array gets passed, an empty Set will be returned. + * + * @param array The array + * @return the filled HashSet of type T + */ + private static Set arrayAsHashSet( @Nullable T[] array ) + { + if ( array == null || array.length == 0 ) + { + return Collections.emptySet(); + } + + Set set = new HashSet( array.length ); + Collections.addAll( set, array ); + + return set; + } + + /** + * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories, + * whereas a fast scan will only have full results for included files, as it ignores directories which can't + * possibly hold any included files/directories. + *

          + * Returns immediately if a slow scan has already been completed. + */ + void slowScan() + { + if ( haveSlowResults ) + { + return; + } + + final String[] excl = dirsExcluded.toArray( new String[dirsExcluded.size()] ); + + final String[] notIncl = dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + + for ( String anExcl : excl ) + { + if ( !couldHoldIncluded( anExcl ) ) + { + scandir( new File( basedir, anExcl ), anExcl + File.separator, false ); + } + } + + for ( String aNotIncl : notIncl ) + { + if ( !couldHoldIncluded( aNotIncl ) ) + { + scandir( new File( basedir, aNotIncl ), aNotIncl + File.separator, false ); + } + } + + haveSlowResults = true; + } + + /** + * Scans the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast Whether or not this call is part of a fast scan. + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ +/** + * Scans the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir + * The directory to scan. Must not be null. + * @param vpath + * The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast + * Whether or not this call is part of a fast scan. + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ +void scandir(@javax.annotation.Nonnull +final java.io.File dir, @javax.annotation.Nonnull +final java.lang.String vpath, final boolean fast) { + java.lang.String[] newfiles = dir.list(); + if (newfiles == null) { + /* two reasons are mentioned in the API docs for File.list (1) dir is not a directory. This is impossible as + we wouldn't get here in this case. (2) an IO error occurred (why doesn't it throw an exception then???) + */ + /* [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... this is affecting the + assembly plugin, fwiw. I will initialize the newfiles array as zero-length for now. NOTE: I can't find + the problematic code, as it appears to come from a native method in UnixFileSystem... + */ + newfiles = new java.lang.String[0]; + // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() ); + } + if (!followSymlinks) { + newfiles = doNotFollowSymbolicLinks(dir, vpath, newfiles); + } + for (final java.lang.String newfile : newfiles) { + final java.lang.String name = vpath + newfile; + final java.io.File file = new java.io.File(dir, newfile); + if (file.isDirectory()) { + if (isIncluded(name)) { + if (!isExcluded(name)) { + if (scanConductor != null) { + scanAction = scanConductor.visitDirectory(name, file); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction) || org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT_DIRECTORY.equals(scanAction)) { + return; + } + } + if (!org.apache.maven.shared.utils.io.ScanConductor.ScanAction.NO_RECURSE.equals(scanAction)) { + dirsIncluded.add(name); + if (fast) { + scandir(file, name + java.io.File.separator, fast); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction)) { + return; + } + } + } + scanAction = null; + } else { + dirsExcluded.add(name); + if (fast && couldHoldIncluded(name)) { + scandir(file, name + java.io.File.separator, fast); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction)) { + return; + } + scanAction = null; + } + } + } else if (fast && couldHoldIncluded(name)) { + if (scanConductor != null) { + scanAction = scanConductor.visitDirectory(name, file); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction) || org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT_DIRECTORY.equals(scanAction)) { + return; + } + } + if (!org.apache.maven.shared.utils.io.ScanConductor.ScanAction.NO_RECURSE.equals(scanAction)) { + dirsNotIncluded.add(name); + scandir(file, name + java.io.File.separator, fast); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction)) { + return; + } + } + scanAction = null; + } + if (!fast) { + scandir(file, name + java.io.File.separator, fast); + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction)) { + return; + } + scanAction = null; + } + } else if (file.isFile()) { + if (isIncluded(name)) { + if (!isExcluded(name)) { + { + scanAction = /* NPEX_NULL_EXP */ + scanConductor.visitFile(name, file); + } + if (org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT.equals(scanAction) || org.apache.maven.shared.utils.io.ScanConductor.ScanAction.ABORT_DIRECTORY.equals(scanAction)) { + return; + } + filesIncluded.add(name); + } else { + filesExcluded.add(name); + } + } else { + filesNotIncluded.add(name); + } + } + } +} + + private String[] doNotFollowSymbolicLinks( final File dir, final String vpath, String[] newfiles ) + { + final List noLinks = new ArrayList(); + for ( final String newfile : newfiles ) + { + try + { + if ( isSymbolicLink( dir, newfile ) ) + { + final String name = vpath + newfile; + final File file = new File( dir, newfile ); + if ( file.isDirectory() ) + { + dirsExcluded.add( name ); + } + else + { + filesExcluded.add( name ); + } + } + else + { + noLinks.add( newfile ); + } + } + catch ( final IOException ioe ) + { + final String msg = + "IOException caught while checking " + "for links, couldn't get cannonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println( msg ); + noLinks.add( newfile ); + } + } + newfiles = noLinks.toArray( new String[noLinks.size()] ); + return newfiles; + } + + /** + * Tests whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + boolean isIncluded( final String name ) + { + return includesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches the start of at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + boolean couldHoldIncluded( @Nonnull final String name ) + { + return includesPatterns.matchesPatternStart( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + boolean isExcluded( @Nonnull final String name ) + { + return excludesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method does not work correctly on Windows. + * @return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to files. + */ + @Deprecated + public String[] getIncludedFiles() + { + if ( filesIncluded == null ) + { + return new String[0]; + } + return filesIncluded.toArray( new String[filesIncluded.size()] ); + } + + /** + * Returns the names of the files which matched none of the include patterns. The names are relative to the base + * directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the files which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedFiles() + { + slowScan(); + return filesNotIncluded.toArray( new String[filesNotIncluded.size()] ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and at least one of the exclude + * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not + * already been completed. + * + * @return the names of the files which matched at least one of the include patterns and at at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedFiles() + { + slowScan(); + return filesExcluded.toArray( new String[filesExcluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method is buggy. Do not depend on it. + * @return the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to directories. + */ + @Deprecated + public String[] getIncludedDirectories() + { + return dirsIncluded.toArray( new String[dirsIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched none of the include patterns. The names are relative to the + * base directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the directories which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedDirectories() + { + slowScan(); + return dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has + * not already been completed. + * + * @return the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedDirectories() + { + slowScan(); + return dirsExcluded.toArray( new String[dirsExcluded.size()] ); + } + + /** + * Adds default exclusions to the current exclusions set. + */ + public void addDefaultExcludes() + { + final int excludesLength = excludes == null ? 0 : excludes.length; + String[] newExcludes; + newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length]; + if ( excludesLength > 0 ) + { + System.arraycopy( excludes, 0, newExcludes, 0, excludesLength ); + } + for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ ) + { + newExcludes[i + excludesLength] = + DEFAULTEXCLUDES[i].replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + excludes = newExcludes; + } + + /** + * Checks whether a given file is a symbolic link. + *

          + * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are identical + * - this may lead to false positives on some platforms. + *

          + * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + */ + boolean isSymbolicLink( final File parent, final String name ) + throws IOException + { + return Files.isSymbolicLink( parent.toPath() ); + } + + private void setupDefaultFilters() + { + if ( includes == null ) + { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + if ( excludes == null ) + { + excludes = new String[0]; + } + } + + + private void setupMatchPatterns() + { + includesPatterns = MatchPatterns.from( includes ); + excludesPatterns = MatchPatterns.from( excludes ); + } + +} diff --git a/Java/maven-shared-utils-DirectoryScanner_686/metadata.json b/Java/maven-shared-utils-DirectoryScanner_686/metadata.json new file mode 100644 index 000000000..baede2741 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_686/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-DirectoryScanner_686", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java", + "line": 664, + "npe_method": "scandir", + "deref_field": "scanConductor", + "npe_class": "DirectoryScanner", + "repo": "maven-shared-utils", + "bug_id": "DirectoryScanner_686" + } +} diff --git a/Java/maven-shared-utils-DirectoryScanner_686/npe.json b/Java/maven-shared-utils-DirectoryScanner_686/npe.json new file mode 100644 index 000000000..f41c8b9a6 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_686/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java", + "line": 664, + "npe_method": "scandir", + "deref_field": "scanConductor", + "npe_class": "DirectoryScanner" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-DirectoryScanner_879/Dockerfile b/Java/maven-shared-utils-DirectoryScanner_879/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_879/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-DirectoryScanner_879/buggy.java b/Java/maven-shared-utils-DirectoryScanner_879/buggy.java new file mode 100644 index 000000000..cd7d21875 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_879/buggy.java @@ -0,0 +1,932 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Class for scanning a directory for files/directories which match certain criteria. + *

          + * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which + * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude + * files based on their filename. + *

          + * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is + * matched against a set of selectors, including special support for matching against filenames with include and and + * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file + * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will + * be placed in the list of files/directories found. + *

          + * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no + * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors + * are supplied, none are applied. + *

          + * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment + * is the name of a directory or file, which is bounded by File.separator ('/' under UNIX, '\' under + * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same + * is done for the pattern against which should be matched. + *

          + * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in + * the pattern, it matches zero or more path segments of the name. + *

          + * There is a special case regarding the use of File.separators at the beginning of the pattern and the + * string to match:
          + * When a pattern starts with a File.separator, the string to match must also start with a + * File.separator. When a pattern does not start with a File.separator, the string to match + * may not start with a File.separator. When one of these rules is not obeyed, the string will not match. + *

          + * When a name path segment is matched against a pattern path segment, the following special characters can be used:
          + * '*' matches zero or more characters
          + * '?' matches one character. + *

          + * Examples: + *

          + * "**\*.class" matches all .class files/dirs in a directory tree. + *

          + * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a + * directory called test. + *

          + * "**" matches everything in a directory tree. + *

          + * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test + * (e.g. "abc\test\def\ghi\XYZ123"). + *

          + * Case sensitivity may be turned off if necessary. By default, it is turned on. + *

          + * Example of usage: + *

          + *

          + * String[] includes = { "**\\*.class" };
          + * String[] excludes = { "modules\\*\\**" };
          + * ds.setIncludes( includes );
          + * ds.setExcludes( excludes );
          + * ds.setBasedir( new File( "test" ) );
          + * ds.setCaseSensitive( true );
          + * ds.scan();
          + *
          + * System.out.println( "FILES:" );
          + * String[] files = ds.getIncludedFiles();
          + * for ( int i = 0; i < files.length; i++ )
          + * {
          + *     System.out.println( files[i] );
          + * }
          + * 
          + *

          + * This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a + * directory called "modules" + *

          + * This class must not be used from multiple Threads concurrently! + * + * @author Arnout J. Kuiper ajkuiper@wxs.nl + * @author Magesh Umasankar + * @author Bruce Atherton + * @author Antoine Levy-Lambert + * @deprecated use {@code java.nio.file.DirectoryStream} and related classes + */ +@Deprecated +public class DirectoryScanner +{ + /** + * Patterns which should be excluded by default. + * + * @see #addDefaultExcludes() + */ + public static final String[] DEFAULTEXCLUDES = { + // Miscellaneous typical temporary files + "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", + + // CVS + "**/CVS", "**/CVS/**", "**/.cvsignore", + + // Subversion + "**/.svn", "**/.svn/**", + + // Arch + "**/.arch-ids", "**/.arch-ids/**", + + // Bazaar + "**/.bzr", "**/.bzr/**", + + // SurroundSCM + "**/.MySCMServerInfo", + + // Mac + "**/.DS_Store", + + // Serena Dimensions Version 10 + "**/.metadata", "**/.metadata/**", + + // Mercurial + "**/.hg", "**/.hg/**", + + // git + "**/.git", "**/.git/**", + + // BitKeeper + "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**", + + // darcs + "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" }; + + /** + * The base directory to be scanned. + */ + private File basedir; + + /** + * The patterns for the files to be included. + */ + private String[] includes; + + /** + * The patterns for the files to be excluded. + */ + private String[] excludes; + + private MatchPatterns excludesPatterns; + + private MatchPatterns includesPatterns; + + + /** + * The files which matched at least one include and no excludes and were selected. + */ + private List filesIncluded; + + /** + * The files which did not match any includes or selectors. + */ + private List filesNotIncluded; + + /** + * The files which matched at least one include and at least one exclude. + */ + private List filesExcluded; + + /** + * The directories which matched at least one include and no excludes and were selected. + */ + private List dirsIncluded; + + /** + * The directories which were found and did not match any includes. + */ + private List dirsNotIncluded; + + /** + * The directories which matched at least one include and at least one exclude. + */ + private List dirsExcluded; + + /** + * Whether or not our results were built by a slow scan. + */ + private boolean haveSlowResults = false; + + /** + * Whether or not the file system should be treated as a case sensitive one. + */ + private boolean isCaseSensitive = true; + + /** + * Whether or not symbolic links should be followed. + * + * + */ + private boolean followSymlinks = true; + + + /** + * A {@link ScanConductor} an control the scanning process. + */ + private ScanConductor scanConductor = null; + + /** + * The last ScanAction. We need to store this in the instance as the scan() method doesn't return + */ + private ScanConductor.ScanAction scanAction = null; + + /** + * Sole constructor. + */ + public DirectoryScanner() + { + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' + * characters are replaced by File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. Must not be null. + */ + public void setBasedir( final String basedir ) + { + setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) ); + } + + /** + * Sets the base directory to be scanned. This is the directory which is scanned recursively. + * + * @param basedir The base directory for scanning. Should not be null. + */ + public void setBasedir( @Nonnull final File basedir ) + { + this.basedir = basedir; + } + + /** + * Returns the base directory to be scanned. This is the directory which is scanned recursively. + * + * @return the base directory to be scanned + */ + public File getBasedir() + { + return basedir; + } + + /** + * Sets whether or not the file system should be regarded as case sensitive. + * + * @param isCaseSensitiveParameter whether or not the file system should be regarded as a case sensitive one + */ + public void setCaseSensitive( final boolean isCaseSensitiveParameter ) + { + this.isCaseSensitive = isCaseSensitiveParameter; + } + + /** + * Sets whether or not symbolic links should be followed. + * + * @param followSymlinks whether or not symbolic links should be followed + */ + public void setFollowSymlinks( final boolean followSymlinks ) + { + this.followSymlinks = followSymlinks; + } + + /** + * Sets the list of include patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param includes A list of include patterns. May be null, indicating that all files should be + * included. If a non-null list is given, all elements must be non-null. + */ + public void setIncludes( final String... includes ) + { + if ( includes == null ) + { + this.includes = null; + } + else + { + this.includes = new String[includes.length]; + for ( int i = 0; i < includes.length; i++ ) + { + String pattern; + pattern = includes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.includes[i] = pattern; + } + } + } + + /** + * Sets the list of exclude patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

          + * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes A list of exclude patterns. May be null, indicating that no files should be + * excluded. If a non-null list is given, all elements must be non-null. + */ + public void setExcludes( final String... excludes ) + { + if ( excludes == null ) + { + this.excludes = null; + } + else + { + this.excludes = new String[excludes.length]; + for ( int i = 0; i < excludes.length; i++ ) + { + String pattern; + pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + if ( pattern.endsWith( File.separator ) ) + { + pattern += "**"; + } + this.excludes[i] = pattern; + } + } + } + + /** + * @param scanConductor {@link #scanConductor} + */ + public void setScanConductor( final ScanConductor scanConductor ) + { + this.scanConductor = scanConductor; + } + + /** + * Scans the base directory for files which match at least one include pattern and don't match any exclude patterns. + * If there are selectors then the files must pass muster there, as well. + * + * @throws IllegalStateException if the base directory was set incorrectly (i.e. if it is null, + * doesn't exist, or isn't a directory). + */ + public void scan() + throws IllegalStateException + { + if ( basedir == null ) + { + throw new IllegalStateException( "No basedir set" ); + } + if ( !basedir.exists() ) + { + throw new IllegalStateException( "basedir " + basedir + " does not exist" ); + } + if ( !basedir.isDirectory() ) + { + throw new IllegalStateException( "basedir " + basedir + " is not a directory" ); + } + + setupDefaultFilters(); + setupMatchPatterns(); + + filesIncluded = new ArrayList(); + filesNotIncluded = new ArrayList(); + filesExcluded = new ArrayList(); + dirsIncluded = new ArrayList(); + dirsNotIncluded = new ArrayList(); + dirsExcluded = new ArrayList(); + scanAction = ScanConductor.ScanAction.CONTINUE; + + if ( isIncluded( "" ) ) + { + if ( !isExcluded( "" ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( "", basedir ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) + || ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + return; + } + } + + dirsIncluded.add( "" ); + } + else + { + dirsExcluded.add( "" ); + } + } + else + { + dirsNotIncluded.add( "" ); + } + scandir( basedir, "", true ); + } + + /** + * Determine the file differences between the currently included files and + * a previously captured list of files. + * This method will not look for a changed in content but sole in the + * list of files given. + *

          + * The method will compare the given array of file Strings with the result + * of the last directory scan. It will execute a {@link #scan()} if no + * result of a previous scan could be found. + *

          + * The result of the diff can be queried by the methods + * {@link DirectoryScanResult#getFilesAdded()} and {@link DirectoryScanResult#getFilesRemoved()} + * + * @param oldFiles the list of previously captured files names. + * @return the result of the directory scan. + */ + public DirectoryScanResult diffIncludedFiles( String... oldFiles ) + { + if ( filesIncluded == null ) + { + // perform a scan if the directory didn't got scanned yet + scan(); + } + + return diffFiles( oldFiles, filesIncluded.toArray( new String[filesIncluded.size()] ) ); + } + + /** + * @param oldFiles array of old files + * @param newFiles array of new files + * @return calculated difference + */ + public static DirectoryScanResult diffFiles( @Nullable String[] oldFiles, @Nullable String[] newFiles ) + { + Set oldFileSet = arrayAsHashSet( oldFiles ); + Set newFileSet = arrayAsHashSet( newFiles ); + + List added = new ArrayList(); + List removed = new ArrayList(); + + for ( String oldFile : oldFileSet ) + { + if ( !newFileSet.contains( oldFile ) ) + { + removed.add( oldFile ); + } + } + + for ( String newFile : newFileSet ) + { + if ( !oldFileSet.contains( newFile ) ) + { + added.add( newFile ); + } + } + + String[] filesAdded = added.toArray( new String[added.size()] ); + String[] filesRemoved = removed.toArray( new String[removed.size()] ); + + return new DirectoryScanResult( filesAdded, filesRemoved ); + } + + + /** + * Take an array of type T and convert it into a HashSet of type T. + * If null or an empty array gets passed, an empty Set will be returned. + * + * @param array The array + * @return the filled HashSet of type T + */ + private static Set arrayAsHashSet( @Nullable T[] array ) + { + if ( array == null || array.length == 0 ) + { + return Collections.emptySet(); + } + + Set set = new HashSet( array.length ); + Collections.addAll( set, array ); + + return set; + } + + /** + * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories, + * whereas a fast scan will only have full results for included files, as it ignores directories which can't + * possibly hold any included files/directories. + *

          + * Returns immediately if a slow scan has already been completed. + */ + void slowScan() + { + if ( haveSlowResults ) + { + return; + } + + final String[] excl = dirsExcluded.toArray( new String[dirsExcluded.size()] ); + + final String[] notIncl = dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + + for ( String anExcl : excl ) + { + if ( !couldHoldIncluded( anExcl ) ) + { + scandir( new File( basedir, anExcl ), anExcl + File.separator, false ); + } + } + + for ( String aNotIncl : notIncl ) + { + if ( !couldHoldIncluded( aNotIncl ) ) + { + scandir( new File( basedir, aNotIncl ), aNotIncl + File.separator, false ); + } + } + + haveSlowResults = true; + } + + /** + * Scans the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast Whether or not this call is part of a fast scan. + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ + void scandir( @Nonnull final File dir, @Nonnull final String vpath, final boolean fast ) + { + String[] newfiles = dir.list(); + + if ( newfiles == null ) + { + /* + * two reasons are mentioned in the API docs for File.list (1) dir is not a directory. This is impossible as + * we wouldn't get here in this case. (2) an IO error occurred (why doesn't it throw an exception then???) + */ + + /* + * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... this is affecting the + * assembly plugin, fwiw. I will initialize the newfiles array as zero-length for now. NOTE: I can't find + * the problematic code, as it appears to come from a native method in UnixFileSystem... + */ + newfiles = new String[0]; + + // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() ); + } + + if ( !followSymlinks ) + { + newfiles = doNotFollowSymbolicLinks( dir, vpath, newfiles ); + } + + for ( final String newfile : newfiles ) + { + final String name = vpath + newfile; + final File file = new File( dir, newfile ); + if ( file.isDirectory() ) + { + if ( isIncluded( name ) ) + { + if ( !isExcluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( name, file ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + } + + if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + dirsIncluded.add( name ); + if ( fast ) + { + scandir( file, name + File.separator, fast ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + } + } + scanAction = null; + + } + else + { + dirsExcluded.add( name ); + if ( fast && couldHoldIncluded( name ) ) + { + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + scanAction = null; + } + } + } + else + { + if ( fast && couldHoldIncluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitDirectory( name, file ); + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + } + if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) + { + dirsNotIncluded.add( name ); + + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + } + scanAction = null; + } + } + if ( !fast ) + { + scandir( file, name + File.separator, fast ); + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) + { + return; + } + scanAction = null; + } + } + else if ( file.isFile() ) + { + if ( isIncluded( name ) ) + { + if ( !isExcluded( name ) ) + { + if ( scanConductor != null ) + { + scanAction = scanConductor.visitFile( name, file ); + } + + if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) + || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) + { + return; + } + + filesIncluded.add( name ); + } + else + { + filesExcluded.add( name ); + } + } + else + { + filesNotIncluded.add( name ); + } + } + } + } + + private String[] doNotFollowSymbolicLinks( final File dir, final String vpath, String[] newfiles ) + { + final List noLinks = new ArrayList(); + for ( final String newfile : newfiles ) + { + try + { + if ( isSymbolicLink( dir, newfile ) ) + { + final String name = vpath + newfile; + final File file = new File( dir, newfile ); + if ( file.isDirectory() ) + { + dirsExcluded.add( name ); + } + else + { + filesExcluded.add( name ); + } + } + else + { + noLinks.add( newfile ); + } + } + catch ( final IOException ioe ) + { + final String msg = + "IOException caught while checking " + "for links, couldn't get cannonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println( msg ); + noLinks.add( newfile ); + } + } + newfiles = noLinks.toArray( new String[noLinks.size()] ); + return newfiles; + } + + /** + * Tests whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + boolean isIncluded( final String name ) + { + return includesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches the start of at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + boolean couldHoldIncluded( @Nonnull final String name ) + { + return includesPatterns.matchesPatternStart( name, isCaseSensitive ); + } + + /** + * Tests whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + boolean isExcluded( @Nonnull final String name ) + { + return excludesPatterns.matches( name, isCaseSensitive ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method does not work correctly on Windows. + * @return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to files. + */ + @Deprecated + public String[] getIncludedFiles() + { + if ( filesIncluded == null ) + { + return new String[0]; + } + return filesIncluded.toArray( new String[filesIncluded.size()] ); + } + + /** + * Returns the names of the files which matched none of the include patterns. The names are relative to the base + * directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the files which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedFiles() + { + slowScan(); + return filesNotIncluded.toArray( new String[filesNotIncluded.size()] ); + } + + /** + * Returns the names of the files which matched at least one of the include patterns and at least one of the exclude + * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not + * already been completed. + * + * @return the names of the files which matched at least one of the include patterns and at at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedFiles() + { + slowScan(); + return filesExcluded.toArray( new String[filesExcluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @deprecated this method is buggy. Do not depend on it. + * @return the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. May also contain symbolic links to directories. + */ + @Deprecated + public String[] getIncludedDirectories() + { + return dirsIncluded.toArray( new String[dirsIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched none of the include patterns. The names are relative to the + * base directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the directories which matched none of the include patterns. + * @see #slowScan + */ + public String[] getNotIncludedDirectories() + { + slowScan(); + return dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); + } + + /** + * Returns the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has + * not already been completed. + * + * @return the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. + * @see #slowScan + */ + public String[] getExcludedDirectories() + { + slowScan(); + return dirsExcluded.toArray( new String[dirsExcluded.size()] ); + } + + /** + * Adds default exclusions to the current exclusions set. + */ +/** + * Adds default exclusions to the current exclusions set. + */ +public void addDefaultExcludes() { + final int excludesLength = /* NPEX_NULL_EXP */ + excludes.length; + java.lang.String[] newExcludes; + newExcludes = new java.lang.String[excludesLength + org.apache.maven.shared.utils.io.DirectoryScanner.DEFAULTEXCLUDES.length]; + if (excludesLength > 0) { + java.lang.System.arraycopy(excludes, 0, newExcludes, 0, excludesLength); + } + for (int i = 0; i < org.apache.maven.shared.utils.io.DirectoryScanner.DEFAULTEXCLUDES.length; i++) { + newExcludes[i + excludesLength] = org.apache.maven.shared.utils.io.DirectoryScanner.DEFAULTEXCLUDES[i].replace('/', java.io.File.separatorChar).replace('\\', java.io.File.separatorChar); + } + excludes = newExcludes; +} + + /** + * Checks whether a given file is a symbolic link. + *

          + * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are identical + * - this may lead to false positives on some platforms. + *

          + * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + */ + boolean isSymbolicLink( final File parent, final String name ) + throws IOException + { + return Files.isSymbolicLink( parent.toPath() ); + } + + private void setupDefaultFilters() + { + if ( includes == null ) + { + // No includes supplied, so set it to 'matches all' + includes = new String[1]; + includes[0] = "**"; + } + if ( excludes == null ) + { + excludes = new String[0]; + } + } + + + private void setupMatchPatterns() + { + includesPatterns = MatchPatterns.from( includes ); + excludesPatterns = MatchPatterns.from( excludes ); + } + +} diff --git a/Java/maven-shared-utils-DirectoryScanner_879/metadata.json b/Java/maven-shared-utils-DirectoryScanner_879/metadata.json new file mode 100644 index 000000000..434dcc301 --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_879/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-DirectoryScanner_879", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java", + "line": 882, + "npe_method": "addDefaultExcludes", + "deref_field": "excludes", + "npe_class": "DirectoryScanner", + "repo": "maven-shared-utils", + "bug_id": "DirectoryScanner_879" + } +} diff --git a/Java/maven-shared-utils-DirectoryScanner_879/npe.json b/Java/maven-shared-utils-DirectoryScanner_879/npe.json new file mode 100644 index 000000000..99f25691a --- /dev/null +++ b/Java/maven-shared-utils-DirectoryScanner_879/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java", + "line": 882, + "npe_method": "addDefaultExcludes", + "deref_field": "excludes", + "npe_class": "DirectoryScanner" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-FileUtils_1348/Dockerfile b/Java/maven-shared-utils-FileUtils_1348/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1348/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-FileUtils_1348/buggy.java b/Java/maven-shared-utils-FileUtils_1348/buggy.java new file mode 100644 index 000000000..19a359d0c --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1348/buggy.java @@ -0,0 +1,2142 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.WillClose; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * This class provides basic facilities for manipulating files and file paths. + *

          + *

          Path-related methods

          + *

          + *

          Methods exist to retrieve the components of a typical file path. For example + * /www/hosted/mysite/index.html, can be broken into: + *

            + *
          • /www/hosted/mysite/index -- retrievable through {@link #removeExtension}
          • + *
          • html -- retrievable through {@link #getExtension}
          • + *
          + *

          + *

          + *

          File-related methods

          + *

          + * There are methods to create a {@link #toFile File from a URL}, copy a + * copy a {@link #copyFile File to another File}, + * copy a {@link #copyURLToFile URL's contents to a File}, + * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File) + * clean} a directory. + *

          + *

          + * Common {@link java.io.File} manipulation routines. + *

          + * Taken from the commons-utils repo. + * Also code from Alexandria's FileUtils. + * And from Avalon Excalibur's IO. + * And from Ant. + * + * @author Kevin A. Burton + * @author Scott Sanders + * @author Daniel Rall + * @author Christoph.Reck + * @author Peter Donald + * @author Jeff Turner + * + */ +public class FileUtils +{ + /** + * protected constructor. + */ + protected FileUtils() + { + // This is a utility class. Normally don't instantiate + } + + /** + * The number of bytes in a kilobyte. + */ + private static final int ONE_KB = 1024; + + /** + * The number of bytes in a megabyte. + */ + private static final int ONE_MB = ONE_KB * ONE_KB; + + /** + * The file copy buffer size (30 MB) + */ + private static final int FILE_COPY_BUFFER_SIZE = ONE_MB * 30; + + /** + * The vm line separator + */ + private static final String FS = System.getProperty( "file.separator" ); + + /** + * Non-valid Characters for naming files, folders under Windows: ":", "*", "?", "\"", "<", ">", "|" + * + * @see + * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13 + */ + private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" }; + + /** + * @return the default excludes pattern + * @see DirectoryScanner#DEFAULTEXCLUDES + */ + @Nonnull public static String[] getDefaultExcludes() + { + return DirectoryScanner.DEFAULTEXCLUDES; + } + + /** + * @return the default excludes pattern as list + * @see #getDefaultExcludes() + */ + @Nonnull public static List getDefaultExcludesAsList() + { + return Arrays.asList( getDefaultExcludes() ); + } + + /** + * @return the default excludes pattern as comma separated string. + * @see DirectoryScanner#DEFAULTEXCLUDES + * @see StringUtils#join(Object[], String) + */ + @Nonnull public static String getDefaultExcludesAsString() + { + return StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, "," ); + } + + /** + * Returns the directory path portion of a file specification string. + * Matches the equally named unix command. + * + * @param path the file path + * @return the directory portion excluding the ending file separator + * @deprecated use {@code Paths.get(path).getParent().getName()} + */ + @Deprecated + @Nonnull public static String dirname( @Nonnull String path ) + { + int i = path.lastIndexOf( File.separator ); + return ( i >= 0 ? path.substring( 0, i ) : "" ); + } + + /** + * Returns the filename portion of a path. + * + * @param path the file path + * @return the filename string with extension + * @deprecated use {@code Paths.get(path).getName()} + */ + @Deprecated + @Nonnull public static String filename( @Nonnull String path ) + { + int i = path.lastIndexOf( File.separator ); + return ( i >= 0 ? path.substring( i + 1 ) : path ); + } + + /** + * Returns the extension portion of a file path. + * This is everything after the last dot '.' in the path (NOT including the dot). + * + * @param path the file path + * @return the extension of the file + * @deprecated use {@code org.apache.commons.io.FilenameUtils.getExtension} + */ + @Deprecated + @Nonnull public static String extension( @Nonnull String path ) + { + // Ensure the last dot is after the last file separator + int lastSep = path.lastIndexOf( File.separatorChar ); + int lastDot; + if ( lastSep < 0 ) + { + lastDot = path.lastIndexOf( '.' ); + } + else + { + lastDot = path.substring( lastSep + 1 ).lastIndexOf( '.' ); + if ( lastDot >= 0 ) + { + lastDot += lastSep + 1; + } + } + + if ( lastDot >= 0 && lastDot > lastSep ) + { + return path.substring( lastDot + 1 ); + } + + return ""; + } + + /** + * Check if a file exists. + * + * @param fileName the file path + * @return true if file exists + * @deprecated use {@code java.io.File.exists()} + */ + @Deprecated + public static boolean fileExists( @Nonnull String fileName ) + { + File file = new File( fileName ); + return file.exists(); + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file path + * @return the file content using the platform encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file))} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull String file ) + throws IOException + { + return fileRead( file, null ); + } + + /** + * @param file the file path + * @param encoding the wanted encoding + * @return the file content using the specified encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(Paths.get(file)), encoding)} + */ + @Deprecated + @Nonnull private static String fileRead( @Nonnull String file, @Nullable String encoding ) + throws IOException + { + return fileRead( new File( file ), encoding ); + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file path + * @return the file content using the platform encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file.toPath()))} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull File file ) + throws IOException + { + return fileRead( file, null ); + } + + /** + * @param file the file path + * @param encoding the wanted encoding + * @return the file content using the specified encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file.toPath()), encoding)} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull File file, @Nullable String encoding ) + throws IOException + { + Charset charset = charset( encoding ); + + StringBuilder buf = new StringBuilder(); + + + try ( Reader reader = Files.newBufferedReader( file.toPath(), charset ) ) + { + int count; + char[] b = new char[512]; + while ( ( count = reader.read( b ) ) >= 0 ) // blocking read + { + buf.append( b, 0, count ); + } + } + + return buf.toString(); + } + + /** + * @param file the file path + * @return the file content lines as String[] using the system default encoding. + * An empty List if the file doesn't exist. + * @throws IOException in case of failure + * @deprecated use {@code java.nio.files.Files.readAllLines()} + */ + @Deprecated + @Nonnull public static String[] fileReadArray( @Nonnull File file ) + throws IOException + { + List lines = loadFile( file ); + + return lines.toArray( new String[lines.size()] ); + } + + /** + * Appends data to a file. The file is created if it does not exist. + * Note: the data is written with platform encoding. + * + * @param fileName the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(), + * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileAppend( @Nonnull String fileName, @Nonnull String data ) + throws IOException + { + fileAppend( fileName, null, data ); + } + + /** + * Appends data to a file. The file will be created if it does not exist. + * + * @param fileName the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(encoding), + * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileAppend( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( OutputStream out = new FileOutputStream( fileName, true ) ) + { + out.write( data.getBytes( charset ) ); + } + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * Note: the data is written with platform encoding + * + * @param fileName the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, + * data.getBytes(), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull String fileName, @Nonnull String data ) + throws IOException + { + fileWrite( fileName, null, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param fileName the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(Paths.get(filename), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + File file = new File( fileName ); + fileWrite( file, encoding, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param file the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull File file, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) ) + { + writer.write( data ); + } + } + + /** + * Writes String array data to a file in the systems default encoding. + * The file will be created if it does not exist. + * + * @param file the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWriteArray( @Nonnull File file, @Nullable String... data ) + throws IOException + { + fileWriteArray( file, null, data ); + } + + /** + * Writes String array data to a file. The file is created if it does not exist. + * + * @param file the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWriteArray( @Nonnull File file, @Nullable String encoding, @Nullable String... data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) ) + { + for ( int i = 0; data != null && i < data.length; i++ ) + { + writer.write( data[i] ); + if ( i < data.length ) + { + writer.write( "\n" ); + } + } + } + } + + /** + * Deletes a file. + * + * @param fileName the path of the file to delete + * @deprecated use {@code Files.delete(Paths.get(fileName))} + */ + @Deprecated + public static void fileDelete( @Nonnull String fileName ) + { + File file = new File( fileName ); + //noinspection ResultOfMethodCallIgnored + deleteLegacyStyle( file ); + } + + /** + * Given a directory and an array of extensions return an array of compliant files. + *

          + * The given extensions should be like "java" and not like ".java". + * + * @param directory the path of the directory + * @param extensions an array of expected extensions + * @return an array of files for the wanted extensions + */ + public static String[] getFilesFromExtension( @Nonnull String directory, @Nonnull String... extensions ) + { + List files = new ArrayList(); + + File currentDir = new File( directory ); + + String[] unknownFiles = currentDir.list(); + + if ( unknownFiles == null ) + { + return new String[0]; + } + + for ( String unknownFile : unknownFiles ) + { + String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFile; + File currentFile = new File( currentFileName ); + + if ( currentFile.isDirectory() ) + { + // ignore all CVS directories... + if ( currentFile.getName().equals( "CVS" ) ) + { + continue; + } + + // ok... traverse into this directory and get all the files... then combine + // them with the current list. + + String[] fetchFiles = getFilesFromExtension( currentFileName, extensions ); + files = blendFilesToList( files, fetchFiles ); + } + else + { + // ok... add the file + + String add = currentFile.getAbsolutePath(); + if ( isValidFile( add, extensions ) ) + { + files.add( add ); + } + } + } + + // ok... move the Vector into the files list... + String[] foundFiles = new String[files.size()]; + files.toArray( foundFiles ); + + return foundFiles; + } + + /** + * Private helper method for getFilesFromExtension() + */ + @Nonnull private static List blendFilesToList( @Nonnull List v, @Nonnull String... files ) + { + Collections.addAll( v, files ); + + return v; + } + + /** + * Checks to see if a file is of a particular type(s). + * Note that if the file does not have an extension, an empty string + * ("") is matched for. + */ + private static boolean isValidFile( @Nonnull String file, @Nonnull String... extensions ) + { + String extension = extension( file ); + + // ok.. now that we have the "extension" go through the current know + // excepted extensions and determine if this one is OK. + + for ( String extension1 : extensions ) + { + if ( extension1.equals( extension ) ) + { + return true; + } + } + + return false; + + } + + /** + * Simple way to make a directory. + * + * @param dir the directory to create + * @throws IllegalArgumentException if the dir contains illegal Windows characters under Windows OS + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + * @deprecated use {@code java.nio.file.Files.createDirectories(Paths.get(dir))} + */ + @Deprecated + public static void mkdir( @Nonnull String dir ) + { + File file = new File( dir ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) + { + throw new IllegalArgumentException( + "The file (" + dir + ") cannot contain any of the following characters: \n" + StringUtils.join( + INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + } + + if ( !file.exists() ) + { + //noinspection ResultOfMethodCallIgnored + file.mkdirs(); + } + } + + /** + * Compare the contents of two files to determine if they are equal or not. + * + * @param file1 the first file + * @param file2 the second file + * @return true if the content of the files are equal or they both don't exist, false otherwise + * @throws IOException if any + */ + public static boolean contentEquals( @Nonnull final File file1, @Nonnull final File file2 ) + throws IOException + { + final boolean file1Exists = file1.exists(); + if ( file1Exists != file2.exists() ) + { + return false; + } + + if ( !file1Exists ) + { + // two not existing files are equal + return true; + } + + if ( file1.isDirectory() || file2.isDirectory() ) + { + // don't want to compare directory contents + return false; + } + + try ( InputStream input1 = new FileInputStream( file1 ); + InputStream input2 = new FileInputStream( file2 ) ) + { + return IOUtil.contentEquals( input1, input2 ); + } + } + + /** + * Convert from a URL to a File. + * + * @param url file URL + * @return the equivalent File object, or null if the URL's protocol + * is not file + */ + @Nullable public static File toFile( @Nullable final URL url ) + { + if ( url == null || !url.getProtocol().equalsIgnoreCase( "file" ) ) + { + return null; + } + + String filename = url.getFile().replace( '/', File.separatorChar ); + int pos = -1; + while ( ( pos = filename.indexOf( '%', pos + 1 ) ) >= 0 ) + { + if ( pos + 2 < filename.length() ) + { + String hexStr = filename.substring( pos + 1, pos + 3 ); + char ch = (char) Integer.parseInt( hexStr, 16 ); + filename = filename.substring( 0, pos ) + ch + filename.substring( pos + 3 ); + } + } + return new File( filename ); + } + + /** + * Convert the array of Files into a list of URLs. + * + * @param files the array of files + * @return the array of URLs + * @throws IOException if an error occurs + */ + @Nonnull public static URL[] toURLs( @Nonnull final File... files ) + throws IOException + { + final URL[] urls = new URL[files.length]; + + for ( int i = 0; i < urls.length; i++ ) + { + urls[i] = files[i].toURI().toURL(); + } + + return urls; + } + + /** + * Remove extension from a path. E.g. + *

          +     * foo.txt    --> foo
          +     * a\b\c.jpg --> a\b\c
          +     * a\b\c     --> a\b\c
          +     * 
          + * + * @param filename the path of the file + * @return the filename minus extension + * @deprecated use {@code org.apache.commons.io.FilenameUtils.removeExtension()} + */ + @Deprecated + @Nonnull public static String removeExtension( @Nonnull final String filename ) + { + String ext = extension( filename ); + + if ( "".equals( ext ) ) + { + return filename; + } + + final int index = filename.lastIndexOf( ext ) - 1; + return filename.substring( 0, index ); + } + + /** + * Get extension from a path. E.g. + * + *
          +     * foo.txt    --> "txt"
          +     * a\b\c.jpg --> "jpg"
          +     * a\b\c     --> ""
          +     * 
          + * + * @param filename the path of the file + * @return the extension of filename or "" if none + * @deprecated use {@code org.apache.commons.io.FilenameUtils.getExtension()} + */ + @Deprecated + @Nonnull public static String getExtension( @Nonnull final String filename ) + { + return extension( filename ); + } + + /** + * Copy file from source to destination. If destinationDirectory does not exist, it + * (and any parent directories) will be created. If a file source in + * destinationDirectory exists, it will be overwritten. + * + * @param source an existing File to copy + * @param destinationDirectory a directory to copy source into + * @throws java.io.FileNotFoundException if source isn't a normal file + * @throws IllegalArgumentException if destinationDirectory isn't a directory + * @throws IOException if source does not exist, the file in + * destinationDirectory cannot be written to, or an IO error + * occurs during copying. + * @deprecated use {@code org.apache.commons.io.FileUtils.copyFileToDirectory()} + */ + @Deprecated + public static void copyFileToDirectory( @Nonnull final File source, @Nonnull final File destinationDirectory ) + throws IOException + { + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFile( source, new File( destinationDirectory, source.getName() ) ); + } + + /** + * Copy file from source to destination only if source is newer than the target file. + * If destinationDirectory does not exist, it + * (and any parent directories) is created. If a file source in + * destinationDirectory exists, it is overwritten. + * + * @param source an existing File to copy + * @param destinationDirectory a directory to copy source into + * @throws java.io.FileNotFoundException if source isn't a normal file + * @throws IllegalArgumentException if destinationDirectory isn't a directory + * @throws IOException if source does not exist, the file in + * destinationDirectory cannot be written to, or an IO error + * occurs during copying + */ + private static void copyFileToDirectoryIfModified( @Nonnull final File source, + @Nonnull final File destinationDirectory ) + throws IOException + { + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFileIfModified( source, new File( destinationDirectory, source.getName() ) ); + } + + + /** + * Copy file from source to destination. The directories up to destination will be + * created if they don't already exist. destination will be overwritten if it + * already exists. + * + * @param source an existing non-directory File to copy bytes from + * @param destination a non-directory File to write bytes to (possibly + * overwriting) + * @throws IOException if source does not exist, destination cannot be + * written to, or an IO error occurs during copying + * @throws java.io.FileNotFoundException if destination is a directory + * @deprecated use {@code java.nio.Files.copy(source.toPath(), destination.toPath(), CopyOptions.NOFOLLOW_LINKS, + * CopyOptions.REPLACE_EXISTING)} + */ + @Deprecated + public static void copyFile( @Nonnull final File source, @Nonnull final File destination ) + throws IOException + { + //check source exists + if ( !source.exists() ) + { + final String message = "File " + source + " does not exist"; + throw new IOException( message ); + } + if ( Files.isSymbolicLink( source.toPath() ) ) + { + File target = Files.readSymbolicLink( source.toPath() ).toFile(); + createSymbolicLink( destination, target ); + return; + } + + //check source != destination, see PLXUTILS-10 + if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) ) + { + //if they are equal, we can exit the method without doing any work + return; + } + + mkdirsFor( destination ); + + doCopyFile( source, destination ); + + if ( source.length() != destination.length() ) + { + final String message = "Failed to copy full contents from " + source + " to " + destination; + throw new IOException( message ); + } + } + + private static void mkdirsFor( @Nonnull File destination ) + { + //does destination directory exist ? + if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) + { + //noinspection ResultOfMethodCallIgnored + destination.getParentFile().mkdirs(); + } + } + + private static void doCopyFile( @Nonnull File source, @Nonnull File destination ) + throws IOException + { + + try ( FileInputStream fis = new FileInputStream( source ); + FileOutputStream fos = new FileOutputStream( destination ); + FileChannel input = fis.getChannel(); + FileChannel output = fos.getChannel() ) + { + + long size = input.size(); + long pos = 0; + long count; + while ( pos < size ) + { + count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos; + pos += output.transferFrom( input, pos, count ); + } + } + + copyFilePermissions( source, destination ); + } + + /** + * Copy file from source to destination only if source timestamp is later than the destination timestamp. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source An existing non-directory File to copy bytes from. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @return true if no problem occured + * @throws IOException if source does not exist, destination cannot be + * written to, or an IO error occurs during copying. + */ + private static boolean copyFileIfModified( @Nonnull final File source, @Nonnull final File destination ) + throws IOException + { + if ( destination.lastModified() < source.lastModified() ) + { + copyFile( source, destination ); + + return true; + } + + return false; + } + + /** + * Copies bytes from the URL source to a file destination. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source A URL to copy bytes from. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @throws IOException if + *
            + *
          • source URL cannot be opened
          • + *
          • destination cannot be written to
          • + *
          • an IO error occurs during copying
          • + *
          + * @deprecated use {@code java.nio.Files.copy(source.openStream(), destination.toPath(), + * CopyOptions.REPLACE_EXISTING)} + */ + public static void copyURLToFile( @Nonnull final URL source, @Nonnull final File destination ) + throws IOException + { + copyStreamToFile( source.openStream(), destination ); + } + + /** + * Copies bytes from the {@link InputStream} source to a file destination. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source An {@link InputStream} to copy bytes from. This stream is + * guaranteed to be closed. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @throws IOException if + *
            + *
          • source cannot be opened
          • + *
          • destination cannot be written to
          • + *
          • an I/O error occurs during copying
          • + *
          + * @deprecated use {@code java.nio.Files.copy(source, destination.toPath(), + * CopyOptions.REPLACE_EXISTING)} + */ + @Deprecated + private static void copyStreamToFile( @Nonnull @WillClose final InputStream source, + @Nonnull final File destination ) + throws IOException + { + // does destination directory exist ? + if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) + { + // noinspection ResultOfMethodCallIgnored + destination.getParentFile().mkdirs(); + } + + // make sure we can write to destination + if ( destination.exists() && !destination.canWrite() ) + { + final String message = "Unable to open file " + destination + " for writing."; + throw new IOException( message ); + } + + try ( OutputStream out = new FileOutputStream( destination ); InputStream in = source ) + { + IOUtil.copy( in, out ); + } + } + + /** + * Normalize a path. + * Eliminates "/../" and "/./" in a string. Returns null if the ..'s went past the + * root. + * Eg: + *
          +     * /foo//               -->     /foo/
          +     * /foo/./              -->     /foo/
          +     * /foo/../bar          -->     /bar
          +     * /foo/../bar/         -->     /bar/
          +     * /foo/../bar/../baz   -->     /baz
          +     * //foo//./bar         -->     /foo/bar
          +     * /../                 -->     null
          +     * 
          + * + * @param path the path to normalize + * @return the normalized String, or null if too many ..'s. + * @deprecated use {@code org.apache.commons.io.FileNameUtils.normalize()} + */ + @Deprecated + @Nonnull public static String normalize( @Nonnull final String path ) + { + String normalized = path; + // Resolve occurrences of "//" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "//" ); + if ( index < 0 ) + { + break; + } + normalized = normalized.substring( 0, index ) + normalized.substring( index + 1 ); + } + + // Resolve occurrences of "/./" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "/./" ); + if ( index < 0 ) + { + break; + } + normalized = normalized.substring( 0, index ) + normalized.substring( index + 2 ); + } + + // Resolve occurrences of "/../" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "/../" ); + if ( index < 0 ) + { + break; + } + if ( index == 0 ) + { + return null; // Trying to go outside our context + } + int index2 = normalized.lastIndexOf( '/', index - 1 ); + normalized = normalized.substring( 0, index2 ) + normalized.substring( index + 3 ); + } + + // Return the normalized path that we have completed + return normalized; + } + + /** + * Resolve a file filename to it's canonical form. If filename is + * relative (doesn't start with /), it will be resolved relative to + * baseFile, otherwise it is treated as a normal root-relative path. + * + * @param baseFile Where to resolve filename from, if filename is + * relative. + * @param filename absolute or relative file path to resolve + * @return the canonical File of filename + */ + @Nonnull public static File resolveFile( final File baseFile, @Nonnull String filename ) + { + String filenm = filename; + if ( '/' != File.separatorChar ) + { + filenm = filename.replace( '/', File.separatorChar ); + } + + if ( '\\' != File.separatorChar ) + { + filenm = filename.replace( '\\', File.separatorChar ); + } + + // deal with absolute files + if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) ) + { + File file = new File( filenm ); + + try + { + file = file.getCanonicalFile(); + } + catch ( final IOException ioe ) + { + // nop + } + + return file; + } + // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips + // them. However, I'm not sure about this UNC stuff. (JT) + final char[] chars = filename.toCharArray(); + final StringBuilder sb = new StringBuilder(); + + //remove duplicate file separators in succession - except + //on win32 at start of filename as UNC filenames can + //be \\AComputer\AShare\myfile.txt + int start = 0; + if ( '\\' == File.separatorChar ) + { + sb.append( filenm.charAt( 0 ) ); + start++; + } + + for ( int i = start; i < chars.length; i++ ) + { + final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1]; + + if ( !doubleSeparator ) + { + sb.append( chars[i] ); + } + } + + filenm = sb.toString(); + + //must be relative + File file = ( new File( baseFile, filenm ) ).getAbsoluteFile(); + + try + { + file = file.getCanonicalFile(); + } + catch ( final IOException ioe ) + { + // nop + } + + return file; + } + + /** + * Delete a file. If file is directory, delete it and all sub-directories. + * + * @param file the file path + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()} + */ + @Deprecated + public static void forceDelete( @Nonnull final String file ) + throws IOException + { + forceDelete( new File( file ) ); + } + + /** + * Delete a file. If file is directory, delete it and all sub-directories. + * + * @param file a file + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()} + */ + @Deprecated + public static void forceDelete( @Nonnull final File file ) + throws IOException + { + if ( file.isDirectory() ) + { + deleteDirectory( file ); + } + else + { + /* + * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a + * symlink whose target does not exist is deleted, too. + */ + boolean filePresent = file.getCanonicalFile().exists(); + if ( !deleteFile( file ) && filePresent ) + { + final String message = "File " + file + " unable to be deleted."; + throw new IOException( message ); + } + } + } + + /** + * Deletes a file. + * + * @param file the file to delete + * @throws IOException if the file cannot be deleted + * @deprecated use {@code java.nio.files.Files.delete(file.toPath())} + */ + @Deprecated + public static void delete( @Nonnull File file ) + throws IOException + { + Files.delete( file.toPath() ); + } + + /** + * @param file the file + * @return true / false + * @deprecated use {@code java.nio.files.Files.delete(file.toPath())} + */ + @Deprecated + public static boolean deleteLegacyStyle( @Nonnull File file ) + { + try + { + Files.delete( file.toPath() ); + return true; + } + catch ( IOException e ) + { + return false; + } + } + + /** + * Accommodate Windows bug encountered in both Sun and IBM JDKs. + * Others possible. If the delete does not work, call System.gc(), + * wait a little and try again. + * + * @param file a file + * @throws IOException if any + */ + private static boolean deleteFile( @Nonnull File file ) + throws IOException + { + if ( file.isDirectory() ) + { + throw new IOException( "File " + file + " isn't a file." ); + } + + if ( !deleteLegacyStyle( file ) ) + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + file = file.getCanonicalFile(); + } + + try + { + Thread.sleep( 10 ); + return deleteLegacyStyle( file ); + } + catch ( InterruptedException ex ) + { + return deleteLegacyStyle( file ); + } + } + + return true; + } + + + /** + * Make a directory. + * + * @param file not null + * @throws IOException if a file already exists with the specified name or the directory is unable to be created + * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS. + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + */ + public static void forceMkdir( @Nonnull final File file ) + throws IOException + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) + { + throw new IllegalArgumentException( + "The file (" + file.getAbsolutePath() + ") cannot contain any of the following characters: \n" + + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + } + + if ( file.exists() ) + { + if ( file.isFile() ) + { + final String message = + "File " + file + " exists and is " + "not a directory. Unable to create directory."; + throw new IOException( message ); + } + } + else + { + if ( !file.mkdirs() ) + { + final String message = "Unable to create directory " + file; + throw new IOException( message ); + } + } + } + + /** + * Recursively delete a directory. + * + * @param directory a directory + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()} + */ + @Deprecated + public static void deleteDirectory( @Nonnull final String directory ) + throws IOException + { + deleteDirectory( new File( directory ) ); + } + + /** + * Recursively delete a directory. + * + * @param directory a directory + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()} + */ + @Deprecated + public static void deleteDirectory( @Nonnull final File directory ) + throws IOException + { + if ( !directory.exists() ) + { + return; + } + + /* try delete the directory before its contents, which will take + * care of any directories that are really symbolic links. + */ + if ( deleteLegacyStyle( directory ) ) + { + return; + } + + cleanDirectory( directory ); + if ( !deleteLegacyStyle( directory ) ) + { + final String message = "Directory " + directory + " unable to be deleted."; + throw new IOException( message ); + } + } + + /** + * Remove all files from a directory without deleting it. + * + * @param directory a directory + * @throws IOException if any. This can leave cleaning in a half-finished state where + * some but not all files have been deleted. + * @deprecated use {@code org.apache.commons.io.FileUtils.cleanDirectory()} + */ + @Deprecated +/** + * Remove all files from a directory without deleting it. + * + * @param directory + * a directory + * @throws IOException + * if any. This can leave cleaning in a half-finished state where + * some but not all files have been deleted. + * @deprecated use {@code org.apache.commons.io.FileUtils.cleanDirectory()} + */ +public static void cleanDirectory(@javax.annotation.Nonnull +final java.io.File directory) throws java.io.IOException { + if (!directory.exists()) { + final java.lang.String message = directory + " does not exist"; + throw new java.lang.IllegalArgumentException(message); + } + if (!directory.isDirectory()) { + final java.lang.String message = directory + " is not a directory"; + throw new java.lang.IllegalArgumentException(message); + } + java.io.IOException exception = null; + final java.io.File[] files = directory.listFiles(); + if (files == null) { + return; + } + for (final java.io.File file : files) { + try { + org.apache.maven.shared.utils.io.FileUtils.forceDelete(file); + } catch (final java.io.IOException ioe) { + exception = ioe; + } + } + { + throw /* NPEX_NULL_EXP */ + exception; + } +} + + /** + * Recursively count size of a directory. + * + * @param directory a directory + * @return size of directory in bytes + * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()} + */ + @Deprecated + public static long sizeOfDirectory( @Nonnull final String directory ) + { + return sizeOfDirectory( new File( directory ) ); + } + + /** + * Recursively count size of a directory. + * + * @param directory a directory + * @return size of directory in bytes + * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()} + */ + @Deprecated + public static long sizeOfDirectory( @Nonnull final File directory ) + { + if ( !directory.exists() ) + { + final String message = directory + " does not exist"; + throw new IllegalArgumentException( message ); + } + + if ( !directory.isDirectory() ) + { + final String message = directory + " is not a directory"; + throw new IllegalArgumentException( message ); + } + + long size = 0; + + final File[] files = directory.listFiles(); + if ( files == null ) + { + throw new IllegalArgumentException( "Problems reading directory" ); + } + + for ( final File file : files ) + { + if ( file.isDirectory() ) + { + size += sizeOfDirectory( file ); + } + else + { + size += file.length(); + } + } + + return size; + } + + /** + * Return the files contained in the directory, using inclusion and exclusion Ant patterns, + * including the directory name in each of the files + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @return a list of File objects + * @throws IOException in case of failure. + * @see #getFileNames(File, String, String, boolean) + */ + @Nonnull + public static List getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes ) + throws IOException + { + return getFiles( directory, includes, excludes, true ); + } + + /** + * Return the files contained in the directory, using inclusion and exclusion Ant patterns + * + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each file + * @return a list of File objects + * @throws IOException in case of failure. + * @see #getFileNames(File, String, String, boolean) + */ + @Nonnull + public static List getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, + boolean includeBasedir ) + throws IOException + { + List fileNames = getFileNames( directory, includes, excludes, includeBasedir ); + + List files = new ArrayList(); + + for ( String filename : fileNames ) + { + files.add( new File( filename ) ); + } + + return files; + } + + /** + * Return a list of files as String depending options. + * This method use case sensitive file name. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @return a list of file names + * @throws IOException in case of failure + */ + @Nonnull public static List getFileNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir ) + throws IOException + { + return getFileNames( directory, includes, excludes, includeBasedir, true ); + } + + /** + * Return a list of files as String depending options. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each String of file + * @param isCaseSensitive true if case sensitive + * @return a list of files as String + * @throws IOException + */ + @Nonnull private static List getFileNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive ) + throws IOException + { + return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false ); + } + + /** + * Return a list of directories as String depending options. + * This method use case sensitive file name. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each String of file + * @return a list of directories as String + * @throws IOException in case of failure. + */ + @Nonnull public static List getDirectoryNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir ) + throws IOException + { + return getDirectoryNames( directory, includes, excludes, includeBasedir, true ); + } + + /** + * Return a list of directories as Strings. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @param isCaseSensitive true if case sensitive + * @return a list of directories as String + * @throws IOException in case of failure + */ + @Nonnull public static List getDirectoryNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive ) + throws IOException + { + return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true ); + } + + /** + * Return a list of file names as Strings. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @param isCaseSensitive true if case sensitive + * @param getFiles true to include regular files + * @param getDirectories true to include directories + * @return a list of file names + */ + @Nonnull public static List getFileAndDirectoryNames( File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive, boolean getFiles, + boolean getDirectories ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + scanner.setBasedir( directory ); + + if ( includes != null ) + { + scanner.setIncludes( StringUtils.split( includes, "," ) ); + } + + if ( excludes != null ) + { + scanner.setExcludes( StringUtils.split( excludes, "," ) ); + } + + scanner.setCaseSensitive( isCaseSensitive ); + + scanner.scan(); + + List list = new ArrayList(); + + if ( getFiles ) + { + String[] files = scanner.getIncludedFiles(); + + for ( String file : files ) + { + if ( includeBasedir ) + { + list.add( directory + FileUtils.FS + file ); + } + else + { + list.add( file ); + } + } + } + + if ( getDirectories ) + { + String[] directories = scanner.getIncludedDirectories(); + + for ( String directory1 : directories ) + { + if ( includeBasedir ) + { + list.add( directory + FileUtils.FS + directory1 ); + } + else + { + list.add( directory1 ); + } + } + } + + return list; + } + + /** + * Copy the contents of a directory into another one. + * + * @param sourceDirectory the source directory + * @param destinationDirectory the target directory + * @throws IOException if any + */ + public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) + throws IOException + { + copyDirectory( sourceDirectory, destinationDirectory, "**", null ); + } + + /** + * Copy the contents of a directory into another one. + * + * @param sourceDirectory the source directory + * @param destinationDirectory the target directory + * @param includes Ant include pattern + * @param excludes Ant exclude pattern + * @throws IOException if any + * @see #getFiles(File, String, String) + */ + public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, + @Nullable String includes, @Nullable String excludes ) + throws IOException + { + if ( !sourceDirectory.exists() ) + { + return; + } + + List files = getFiles( sourceDirectory, includes, excludes ); + + for ( File file : files ) + { + copyFileToDirectory( file, destinationDirectory ); + } + } + + /** + * Copies an entire directory structure. + *

          + * Note: + *

            + *
          • It will include empty directories. + *
          • The sourceDirectory must exist. + *
          + * + * @param sourceDirectory the source dir + * @param destinationDirectory the target dir + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.copyDirectory()} + */ + @Deprecated + public static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) + throws IOException + { + copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, false ); + } + + private static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, + File rootDestinationDirectory, boolean onlyModifiedFiles ) + throws IOException + { + //noinspection ConstantConditions + if ( sourceDirectory == null ) + { + throw new IOException( "source directory can't be null." ); + } + + //noinspection ConstantConditions + if ( destinationDirectory == null ) + { + throw new IOException( "destination directory can't be null." ); + } + + if ( sourceDirectory.equals( destinationDirectory ) ) + { + throw new IOException( "source and destination are the same directory." ); + } + + if ( !sourceDirectory.exists() ) + { + throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); + } + + File[] files = sourceDirectory.listFiles(); + + if ( files == null ) + { + return; + } + + String sourcePath = sourceDirectory.getAbsolutePath(); + + for ( File file : files ) + { + if ( file.equals( rootDestinationDirectory ) ) + { + // We don't copy the destination directory in itself + continue; + } + + String dest = file.getAbsolutePath(); + + dest = dest.substring( sourcePath.length() + 1 ); + + File destination = new File( destinationDirectory, dest ); + + if ( file.isFile() ) + { + destination = destination.getParentFile(); + + if ( onlyModifiedFiles ) + { + copyFileToDirectoryIfModified( file, destination ); + } + else + { + copyFileToDirectory( file, destination ); + } + } + else if ( file.isDirectory() ) + { + if ( !destination.exists() && !destination.mkdirs() ) + { + throw new IOException( + "Could not create destination directory '" + destination.getAbsolutePath() + "'." ); + } + + copyDirectoryStructure( file, destination, rootDestinationDirectory, onlyModifiedFiles ); + } + else + { + throw new IOException( "Unknown file type: " + file.getAbsolutePath() ); + } + } + } + + /** + * Renames a file, even if that involves crossing file system boundaries. + *

          + *

          This will remove to (if it exists), ensure that + * to's parent directory exists and move + * from, which involves deleting from as + * well.

          + * + * @param from the file to move + * @param to the new file name + * @throws IOException if anything bad happens during this process. + * Note that to may have been deleted already when this happens. + * @deprecated use {@code java.nio.Files.move()} + */ + @Deprecated + public static void rename( @Nonnull File from, @Nonnull File to ) + throws IOException + { + if ( to.exists() && !deleteLegacyStyle( to ) ) + { + throw new IOException( "Failed to delete " + to + " while trying to rename " + from ); + } + + File parent = to.getParentFile(); + if ( parent != null && !parent.exists() && !parent.mkdirs() ) + { + throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from ); + } + + if ( !from.renameTo( to ) ) + { + copyFile( from, to ); + if ( !deleteLegacyStyle( from ) ) + { + throw new IOException( "Failed to delete " + from + " while trying to rename it." ); + } + } + } + + /** + * Create a temporary file in a given directory. + *

          + *

          The file denoted by the returned abstract pathname did not + * exist before this method was invoked, any subsequent invocation + * of this method will yield a different file name.

          + *

          + * The filename is prefixNNNNNsuffix where NNNN is a random number + *

          + *

          This method is different to {@link File#createTempFile(String, String, File)} + * as it doesn't create the file itself. + * It uses the location pointed to by java.io.tmpdir + * when the parentDir attribute is null.

          + *

          To automatically delete the file created by this method, use the + * {@link File#deleteOnExit()} method.

          + * + * @param prefix prefix before the random number + * @param suffix file extension; include the '.' + * @param parentDir directory to create the temporary file in -java.io.tmpdir + * used if not specified + * @return a File reference to the new temporary file. + * @deprecated use {@code java.nio.Files.createTempFile()} + */ + @Deprecated + public static File createTempFile( @Nonnull String prefix, @Nonnull String suffix, @Nullable File parentDir ) + { + File result; + String parent = System.getProperty( "java.io.tmpdir" ); + if ( parentDir != null ) + { + parent = parentDir.getPath(); + } + DecimalFormat fmt = new DecimalFormat( "#####" ); + SecureRandom secureRandom = new SecureRandom(); + long secureInitializer = secureRandom.nextLong(); + Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() ); + do + { + result = new File( parent, prefix + fmt.format( positiveRandom( rand ) ) + suffix ); + } + while ( result.exists() ); + + return result; + } + + private static int positiveRandom( Random rand ) + { + int a = rand.nextInt(); + while ( a == Integer.MIN_VALUE ) + { + a = rand.nextInt(); + } + return Math.abs( a ); + } + + /** + * If wrappers is null or empty, the file will be copied only if to.lastModified() < from.lastModified() + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} + * @throws IOException if an IO error occurs during copying or filtering + */ + public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, + @Nullable FilterWrapper... wrappers ) + throws IOException + { + copyFile( from, to, encoding, wrappers, false ); + } + + /** + * Wrapper class for Filter. + */ + public abstract static class FilterWrapper + { + /** + * @param fileReader {@link Reader} + * @return the Reader instance + */ + public abstract Reader getReader( Reader fileReader ); + } + + /** + * If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if + * overwrite is true + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} + * @param overwrite if true and wrappers is null or empty, the file will be copied even if + * to.lastModified() < from.lastModified() + * @throws IOException if an IO error occurs during copying or filtering + */ + public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, + @Nullable FilterWrapper[] wrappers, boolean overwrite ) + throws IOException + { + if ( wrappers == null || wrappers.length == 0 ) + { + if ( overwrite || to.lastModified() < from.lastModified() ) + { + copyFile( from, to ); + } + } + else + { + Charset charset = charset( encoding ); + + // buffer so it isn't reading a byte at a time! + try ( Reader fileReader = Files.newBufferedReader( from.toPath(), charset ) ) + { + Reader wrapped = fileReader; + for ( FilterWrapper wrapper : wrappers ) + { + wrapped = wrapper.getReader( wrapped ); + } + + if ( overwrite || !to.exists() ) + { + try ( Writer fileWriter = Files.newBufferedWriter( to.toPath(), charset ) ) + { + IOUtil.copy( wrapped, fileWriter ); + } + } + else + { + CharsetEncoder encoder = charset.newEncoder(); + + int totalBufferSize = FILE_COPY_BUFFER_SIZE; + + int charBufferSize = ( int ) Math.floor( totalBufferSize / ( 2 + 2 * encoder.maxBytesPerChar() ) ); + int byteBufferSize = ( int ) Math.ceil( charBufferSize * encoder.maxBytesPerChar() ); + + CharBuffer newChars = CharBuffer.allocate( charBufferSize ); + ByteBuffer newBytes = ByteBuffer.allocate( byteBufferSize ); + ByteBuffer existingBytes = ByteBuffer.allocate( byteBufferSize ); + + CoderResult coderResult; + int existingRead; + boolean writing = false; + + try ( final RandomAccessFile existing = new RandomAccessFile( to, "rw" ) ) + { + int n; + while ( -1 != ( n = wrapped.read( newChars ) ) ) + { + ( ( Buffer ) newChars ).flip(); + + coderResult = encoder.encode( newChars, newBytes, n != 0 ); + if ( coderResult.isError() ) + { + coderResult.throwException(); + } + + ( ( Buffer ) newBytes ).flip(); + + if ( !writing ) + { + existingRead = existing.read( existingBytes.array(), 0, newBytes.remaining() ); + ( ( Buffer ) existingBytes ).position( existingRead ); + ( ( Buffer ) existingBytes ).flip(); + + if ( newBytes.compareTo( existingBytes ) != 0 ) + { + writing = true; + if ( existingRead > 0 ) + { + existing.seek( existing.getFilePointer() - existingRead ); + } + } + } + + if ( writing ) + { + existing.write( newBytes.array(), 0, newBytes.remaining() ); + } + + ( ( Buffer ) newChars ).clear(); + ( ( Buffer ) newBytes ).clear(); + ( ( Buffer ) existingBytes ).clear(); + } + + if ( existing.length() > existing.getFilePointer() ) + { + existing.setLength( existing.getFilePointer() ); + } + } + } + } + } + + copyFilePermissions( from, to ); + } + + /** + * Attempts to copy file permissions from the source to the destination file. + * Initially attempts to copy posix file permissions, assuming that the files are both on posix filesystems. + * If the initial attempts fail then a second attempt using less precise permissions model. + * Note that permissions are copied on a best-efforts basis, + * failure to copy permissions will not result in an exception. + * + * @param source the file to copy permissions from. + * @param destination the file to copy permissions to. + */ + private static void copyFilePermissions( @Nonnull File source, @Nonnull File destination ) + throws IOException + { + try + { + // attempt to copy posix file permissions + Files.setPosixFilePermissions( + destination.toPath(), + Files.getPosixFilePermissions( source.toPath() ) + ); + } + catch ( UnsupportedOperationException e ) + { + // fallback to setting partial permissions + destination.setExecutable( source.canExecute() ); + destination.setReadable( source.canRead() ); + destination.setWritable( source.canWrite() ); + } + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file + * @return a List containing every every line not starting with # and not empty + * @throws IOException if any + * @deprecated assumes the platform default character set + */ + @Deprecated + @Nonnull public static List loadFile( @Nonnull File file ) + throws IOException + { + List lines = new ArrayList(); + + if ( file.exists() ) + { + try ( BufferedReader reader = Files.newBufferedReader( file.toPath(), Charset.defaultCharset() ) ) + { + for ( String line = reader.readLine(); line != null; line = reader.readLine() ) + { + line = line.trim(); + if ( !line.startsWith( "#" ) && line.length() != 0 ) + { + lines.add( line ); + } + } + } + } + + return lines; + + } + + /** + * Returns the named charset or the default charset. + * @param encoding the name or alias of the charset, null or empty + * @return A charset object for the named or default charset. + */ + private static Charset charset( String encoding ) + { + if ( encoding == null || encoding.isEmpty() ) + { + return Charset.defaultCharset(); + } + else + { + return Charset.forName( encoding ); + } + } + + /** + * For Windows OS, check if the file name contains any of the following characters: + * ":", "*", "?", "\"", "<", ">", "|" + * + * @param f not null file + * @return false if the file path contains any of forbidden Windows characters, + * true if the Os is not Windows or if the file path respect the Windows constraints. + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + */ + private static boolean isValidWindowsFileName( @Nonnull File f ) + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 ) + { + return false; + } + + if ( f.getParentFile() != null ) + { + return isValidWindowsFileName( f.getParentFile() ); + } + } + + return true; + } + + /** + * Checks whether a given file is a symbolic link. + * + * @param file the file to check + * @throws IOException in case of failure. + * @return true if symbolic link false otherwise. + * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())} + */ + @Deprecated + public static boolean isSymbolicLink( @Nonnull final File file ) + throws IOException + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * Checks whether a given file is a symbolic link. + * + * @param file the file to check + * @return true if and only if we reliably can say this is a symlink + * + * @throws IOException in case of failure + * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())} + */ + @Deprecated + public static boolean isSymbolicLinkForSure( @Nonnull final File file ) + throws IOException + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * Create a new symbolic link, possibly replacing an existing symbolic link. + * + * @param symlink the link name + * @param target the target + * @return the linked file + * @throws IOException in case of an error + * @see {@code java.nio.file.Files.createSymbolicLink(Path)} which creates a new + * symbolic link but does not replace exsiting symbolic links + */ + @Nonnull public static File createSymbolicLink( @Nonnull File symlink, @Nonnull File target ) + throws IOException + { + final Path symlinkPath = symlink.toPath(); + + if ( Files.exists( symlinkPath ) ) + { + if ( target.equals( Files.readSymbolicLink( symlinkPath ).toFile() ) ) + { + return symlink; + } + + Files.delete( symlinkPath ); + } + + return Files.createSymbolicLink( symlinkPath, target.toPath() ).toFile(); + } +} diff --git a/Java/maven-shared-utils-FileUtils_1348/metadata.json b/Java/maven-shared-utils-FileUtils_1348/metadata.json new file mode 100644 index 000000000..e4791e1fd --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1348/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-FileUtils_1348", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/FileUtils.java", + "line": 1346, + "npe_method": "cleanDirectory", + "deref_field": "exception", + "npe_class": "FileUtils", + "repo": "maven-shared-utils", + "bug_id": "FileUtils_1348" + } +} diff --git a/Java/maven-shared-utils-FileUtils_1348/npe.json b/Java/maven-shared-utils-FileUtils_1348/npe.json new file mode 100644 index 000000000..a690aef66 --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1348/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/FileUtils.java", + "line": 1346, + "npe_method": "cleanDirectory", + "deref_field": "exception", + "npe_class": "FileUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-FileUtils_1553/Dockerfile b/Java/maven-shared-utils-FileUtils_1553/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1553/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-FileUtils_1553/buggy.java b/Java/maven-shared-utils-FileUtils_1553/buggy.java new file mode 100644 index 000000000..2b1abf789 --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1553/buggy.java @@ -0,0 +1,2141 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.WillClose; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * This class provides basic facilities for manipulating files and file paths. + *

          + *

          Path-related methods

          + *

          + *

          Methods exist to retrieve the components of a typical file path. For example + * /www/hosted/mysite/index.html, can be broken into: + *

            + *
          • /www/hosted/mysite/index -- retrievable through {@link #removeExtension}
          • + *
          • html -- retrievable through {@link #getExtension}
          • + *
          + *

          + *

          + *

          File-related methods

          + *

          + * There are methods to create a {@link #toFile File from a URL}, copy a + * copy a {@link #copyFile File to another File}, + * copy a {@link #copyURLToFile URL's contents to a File}, + * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File) + * clean} a directory. + *

          + *

          + * Common {@link java.io.File} manipulation routines. + *

          + * Taken from the commons-utils repo. + * Also code from Alexandria's FileUtils. + * And from Avalon Excalibur's IO. + * And from Ant. + * + * @author Kevin A. Burton + * @author Scott Sanders + * @author Daniel Rall + * @author Christoph.Reck + * @author Peter Donald + * @author Jeff Turner + * + */ +public class FileUtils +{ + /** + * protected constructor. + */ + protected FileUtils() + { + // This is a utility class. Normally don't instantiate + } + + /** + * The number of bytes in a kilobyte. + */ + private static final int ONE_KB = 1024; + + /** + * The number of bytes in a megabyte. + */ + private static final int ONE_MB = ONE_KB * ONE_KB; + + /** + * The file copy buffer size (30 MB) + */ + private static final int FILE_COPY_BUFFER_SIZE = ONE_MB * 30; + + /** + * The vm line separator + */ + private static final String FS = System.getProperty( "file.separator" ); + + /** + * Non-valid Characters for naming files, folders under Windows: ":", "*", "?", "\"", "<", ">", "|" + * + * @see + * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13 + */ + private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" }; + + /** + * @return the default excludes pattern + * @see DirectoryScanner#DEFAULTEXCLUDES + */ + @Nonnull public static String[] getDefaultExcludes() + { + return DirectoryScanner.DEFAULTEXCLUDES; + } + + /** + * @return the default excludes pattern as list + * @see #getDefaultExcludes() + */ + @Nonnull public static List getDefaultExcludesAsList() + { + return Arrays.asList( getDefaultExcludes() ); + } + + /** + * @return the default excludes pattern as comma separated string. + * @see DirectoryScanner#DEFAULTEXCLUDES + * @see StringUtils#join(Object[], String) + */ + @Nonnull public static String getDefaultExcludesAsString() + { + return StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, "," ); + } + + /** + * Returns the directory path portion of a file specification string. + * Matches the equally named unix command. + * + * @param path the file path + * @return the directory portion excluding the ending file separator + * @deprecated use {@code Paths.get(path).getParent().getName()} + */ + @Deprecated + @Nonnull public static String dirname( @Nonnull String path ) + { + int i = path.lastIndexOf( File.separator ); + return ( i >= 0 ? path.substring( 0, i ) : "" ); + } + + /** + * Returns the filename portion of a path. + * + * @param path the file path + * @return the filename string with extension + * @deprecated use {@code Paths.get(path).getName()} + */ + @Deprecated + @Nonnull public static String filename( @Nonnull String path ) + { + int i = path.lastIndexOf( File.separator ); + return ( i >= 0 ? path.substring( i + 1 ) : path ); + } + + /** + * Returns the extension portion of a file path. + * This is everything after the last dot '.' in the path (NOT including the dot). + * + * @param path the file path + * @return the extension of the file + * @deprecated use {@code org.apache.commons.io.FilenameUtils.getExtension} + */ + @Deprecated + @Nonnull public static String extension( @Nonnull String path ) + { + // Ensure the last dot is after the last file separator + int lastSep = path.lastIndexOf( File.separatorChar ); + int lastDot; + if ( lastSep < 0 ) + { + lastDot = path.lastIndexOf( '.' ); + } + else + { + lastDot = path.substring( lastSep + 1 ).lastIndexOf( '.' ); + if ( lastDot >= 0 ) + { + lastDot += lastSep + 1; + } + } + + if ( lastDot >= 0 && lastDot > lastSep ) + { + return path.substring( lastDot + 1 ); + } + + return ""; + } + + /** + * Check if a file exists. + * + * @param fileName the file path + * @return true if file exists + * @deprecated use {@code java.io.File.exists()} + */ + @Deprecated + public static boolean fileExists( @Nonnull String fileName ) + { + File file = new File( fileName ); + return file.exists(); + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file path + * @return the file content using the platform encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file))} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull String file ) + throws IOException + { + return fileRead( file, null ); + } + + /** + * @param file the file path + * @param encoding the wanted encoding + * @return the file content using the specified encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(Paths.get(file)), encoding)} + */ + @Deprecated + @Nonnull private static String fileRead( @Nonnull String file, @Nullable String encoding ) + throws IOException + { + return fileRead( new File( file ), encoding ); + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file path + * @return the file content using the platform encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file.toPath()))} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull File file ) + throws IOException + { + return fileRead( file, null ); + } + + /** + * @param file the file path + * @param encoding the wanted encoding + * @return the file content using the specified encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file.toPath()), encoding)} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull File file, @Nullable String encoding ) + throws IOException + { + Charset charset = charset( encoding ); + + StringBuilder buf = new StringBuilder(); + + + try ( Reader reader = Files.newBufferedReader( file.toPath(), charset ) ) + { + int count; + char[] b = new char[512]; + while ( ( count = reader.read( b ) ) >= 0 ) // blocking read + { + buf.append( b, 0, count ); + } + } + + return buf.toString(); + } + + /** + * @param file the file path + * @return the file content lines as String[] using the system default encoding. + * An empty List if the file doesn't exist. + * @throws IOException in case of failure + * @deprecated use {@code java.nio.files.Files.readAllLines()} + */ + @Deprecated + @Nonnull public static String[] fileReadArray( @Nonnull File file ) + throws IOException + { + List lines = loadFile( file ); + + return lines.toArray( new String[lines.size()] ); + } + + /** + * Appends data to a file. The file is created if it does not exist. + * Note: the data is written with platform encoding. + * + * @param fileName the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(), + * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileAppend( @Nonnull String fileName, @Nonnull String data ) + throws IOException + { + fileAppend( fileName, null, data ); + } + + /** + * Appends data to a file. The file will be created if it does not exist. + * + * @param fileName the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(encoding), + * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileAppend( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( OutputStream out = new FileOutputStream( fileName, true ) ) + { + out.write( data.getBytes( charset ) ); + } + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * Note: the data is written with platform encoding + * + * @param fileName the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, + * data.getBytes(), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull String fileName, @Nonnull String data ) + throws IOException + { + fileWrite( fileName, null, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param fileName the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(Paths.get(filename), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + File file = new File( fileName ); + fileWrite( file, encoding, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param file the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull File file, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) ) + { + writer.write( data ); + } + } + + /** + * Writes String array data to a file in the systems default encoding. + * The file will be created if it does not exist. + * + * @param file the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWriteArray( @Nonnull File file, @Nullable String... data ) + throws IOException + { + fileWriteArray( file, null, data ); + } + + /** + * Writes String array data to a file. The file is created if it does not exist. + * + * @param file the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWriteArray( @Nonnull File file, @Nullable String encoding, @Nullable String... data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) ) + { + for ( int i = 0; data != null && i < data.length; i++ ) + { + writer.write( data[i] ); + if ( i < data.length ) + { + writer.write( "\n" ); + } + } + } + } + + /** + * Deletes a file. + * + * @param fileName the path of the file to delete + * @deprecated use {@code Files.delete(Paths.get(fileName))} + */ + @Deprecated + public static void fileDelete( @Nonnull String fileName ) + { + File file = new File( fileName ); + //noinspection ResultOfMethodCallIgnored + deleteLegacyStyle( file ); + } + + /** + * Given a directory and an array of extensions return an array of compliant files. + *

          + * The given extensions should be like "java" and not like ".java". + * + * @param directory the path of the directory + * @param extensions an array of expected extensions + * @return an array of files for the wanted extensions + */ + public static String[] getFilesFromExtension( @Nonnull String directory, @Nonnull String... extensions ) + { + List files = new ArrayList(); + + File currentDir = new File( directory ); + + String[] unknownFiles = currentDir.list(); + + if ( unknownFiles == null ) + { + return new String[0]; + } + + for ( String unknownFile : unknownFiles ) + { + String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFile; + File currentFile = new File( currentFileName ); + + if ( currentFile.isDirectory() ) + { + // ignore all CVS directories... + if ( currentFile.getName().equals( "CVS" ) ) + { + continue; + } + + // ok... traverse into this directory and get all the files... then combine + // them with the current list. + + String[] fetchFiles = getFilesFromExtension( currentFileName, extensions ); + files = blendFilesToList( files, fetchFiles ); + } + else + { + // ok... add the file + + String add = currentFile.getAbsolutePath(); + if ( isValidFile( add, extensions ) ) + { + files.add( add ); + } + } + } + + // ok... move the Vector into the files list... + String[] foundFiles = new String[files.size()]; + files.toArray( foundFiles ); + + return foundFiles; + } + + /** + * Private helper method for getFilesFromExtension() + */ + @Nonnull private static List blendFilesToList( @Nonnull List v, @Nonnull String... files ) + { + Collections.addAll( v, files ); + + return v; + } + + /** + * Checks to see if a file is of a particular type(s). + * Note that if the file does not have an extension, an empty string + * ("") is matched for. + */ + private static boolean isValidFile( @Nonnull String file, @Nonnull String... extensions ) + { + String extension = extension( file ); + + // ok.. now that we have the "extension" go through the current know + // excepted extensions and determine if this one is OK. + + for ( String extension1 : extensions ) + { + if ( extension1.equals( extension ) ) + { + return true; + } + } + + return false; + + } + + /** + * Simple way to make a directory. + * + * @param dir the directory to create + * @throws IllegalArgumentException if the dir contains illegal Windows characters under Windows OS + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + * @deprecated use {@code java.nio.file.Files.createDirectories(Paths.get(dir))} + */ + @Deprecated + public static void mkdir( @Nonnull String dir ) + { + File file = new File( dir ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) + { + throw new IllegalArgumentException( + "The file (" + dir + ") cannot contain any of the following characters: \n" + StringUtils.join( + INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + } + + if ( !file.exists() ) + { + //noinspection ResultOfMethodCallIgnored + file.mkdirs(); + } + } + + /** + * Compare the contents of two files to determine if they are equal or not. + * + * @param file1 the first file + * @param file2 the second file + * @return true if the content of the files are equal or they both don't exist, false otherwise + * @throws IOException if any + */ + public static boolean contentEquals( @Nonnull final File file1, @Nonnull final File file2 ) + throws IOException + { + final boolean file1Exists = file1.exists(); + if ( file1Exists != file2.exists() ) + { + return false; + } + + if ( !file1Exists ) + { + // two not existing files are equal + return true; + } + + if ( file1.isDirectory() || file2.isDirectory() ) + { + // don't want to compare directory contents + return false; + } + + try ( InputStream input1 = new FileInputStream( file1 ); + InputStream input2 = new FileInputStream( file2 ) ) + { + return IOUtil.contentEquals( input1, input2 ); + } + } + + /** + * Convert from a URL to a File. + * + * @param url file URL + * @return the equivalent File object, or null if the URL's protocol + * is not file + */ + @Nullable public static File toFile( @Nullable final URL url ) + { + if ( url == null || !url.getProtocol().equalsIgnoreCase( "file" ) ) + { + return null; + } + + String filename = url.getFile().replace( '/', File.separatorChar ); + int pos = -1; + while ( ( pos = filename.indexOf( '%', pos + 1 ) ) >= 0 ) + { + if ( pos + 2 < filename.length() ) + { + String hexStr = filename.substring( pos + 1, pos + 3 ); + char ch = (char) Integer.parseInt( hexStr, 16 ); + filename = filename.substring( 0, pos ) + ch + filename.substring( pos + 3 ); + } + } + return new File( filename ); + } + + /** + * Convert the array of Files into a list of URLs. + * + * @param files the array of files + * @return the array of URLs + * @throws IOException if an error occurs + */ + @Nonnull public static URL[] toURLs( @Nonnull final File... files ) + throws IOException + { + final URL[] urls = new URL[files.length]; + + for ( int i = 0; i < urls.length; i++ ) + { + urls[i] = files[i].toURI().toURL(); + } + + return urls; + } + + /** + * Remove extension from a path. E.g. + *

          +     * foo.txt    --> foo
          +     * a\b\c.jpg --> a\b\c
          +     * a\b\c     --> a\b\c
          +     * 
          + * + * @param filename the path of the file + * @return the filename minus extension + * @deprecated use {@code org.apache.commons.io.FilenameUtils.removeExtension()} + */ + @Deprecated + @Nonnull public static String removeExtension( @Nonnull final String filename ) + { + String ext = extension( filename ); + + if ( "".equals( ext ) ) + { + return filename; + } + + final int index = filename.lastIndexOf( ext ) - 1; + return filename.substring( 0, index ); + } + + /** + * Get extension from a path. E.g. + * + *
          +     * foo.txt    --> "txt"
          +     * a\b\c.jpg --> "jpg"
          +     * a\b\c     --> ""
          +     * 
          + * + * @param filename the path of the file + * @return the extension of filename or "" if none + * @deprecated use {@code org.apache.commons.io.FilenameUtils.getExtension()} + */ + @Deprecated + @Nonnull public static String getExtension( @Nonnull final String filename ) + { + return extension( filename ); + } + + /** + * Copy file from source to destination. If destinationDirectory does not exist, it + * (and any parent directories) will be created. If a file source in + * destinationDirectory exists, it will be overwritten. + * + * @param source an existing File to copy + * @param destinationDirectory a directory to copy source into + * @throws java.io.FileNotFoundException if source isn't a normal file + * @throws IllegalArgumentException if destinationDirectory isn't a directory + * @throws IOException if source does not exist, the file in + * destinationDirectory cannot be written to, or an IO error + * occurs during copying. + * @deprecated use {@code org.apache.commons.io.FileUtils.copyFileToDirectory()} + */ + @Deprecated + public static void copyFileToDirectory( @Nonnull final File source, @Nonnull final File destinationDirectory ) + throws IOException + { + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFile( source, new File( destinationDirectory, source.getName() ) ); + } + + /** + * Copy file from source to destination only if source is newer than the target file. + * If destinationDirectory does not exist, it + * (and any parent directories) is created. If a file source in + * destinationDirectory exists, it is overwritten. + * + * @param source an existing File to copy + * @param destinationDirectory a directory to copy source into + * @throws java.io.FileNotFoundException if source isn't a normal file + * @throws IllegalArgumentException if destinationDirectory isn't a directory + * @throws IOException if source does not exist, the file in + * destinationDirectory cannot be written to, or an IO error + * occurs during copying + */ + private static void copyFileToDirectoryIfModified( @Nonnull final File source, + @Nonnull final File destinationDirectory ) + throws IOException + { + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFileIfModified( source, new File( destinationDirectory, source.getName() ) ); + } + + + /** + * Copy file from source to destination. The directories up to destination will be + * created if they don't already exist. destination will be overwritten if it + * already exists. + * + * @param source an existing non-directory File to copy bytes from + * @param destination a non-directory File to write bytes to (possibly + * overwriting) + * @throws IOException if source does not exist, destination cannot be + * written to, or an IO error occurs during copying + * @throws java.io.FileNotFoundException if destination is a directory + * @deprecated use {@code java.nio.Files.copy(source.toPath(), destination.toPath(), CopyOptions.NOFOLLOW_LINKS, + * CopyOptions.REPLACE_EXISTING)} + */ + @Deprecated + public static void copyFile( @Nonnull final File source, @Nonnull final File destination ) + throws IOException + { + //check source exists + if ( !source.exists() ) + { + final String message = "File " + source + " does not exist"; + throw new IOException( message ); + } + if ( Files.isSymbolicLink( source.toPath() ) ) + { + File target = Files.readSymbolicLink( source.toPath() ).toFile(); + createSymbolicLink( destination, target ); + return; + } + + //check source != destination, see PLXUTILS-10 + if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) ) + { + //if they are equal, we can exit the method without doing any work + return; + } + + mkdirsFor( destination ); + + doCopyFile( source, destination ); + + if ( source.length() != destination.length() ) + { + final String message = "Failed to copy full contents from " + source + " to " + destination; + throw new IOException( message ); + } + } + + private static void mkdirsFor( @Nonnull File destination ) + { + //does destination directory exist ? + if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) + { + //noinspection ResultOfMethodCallIgnored + destination.getParentFile().mkdirs(); + } + } + + private static void doCopyFile( @Nonnull File source, @Nonnull File destination ) + throws IOException + { + + try ( FileInputStream fis = new FileInputStream( source ); + FileOutputStream fos = new FileOutputStream( destination ); + FileChannel input = fis.getChannel(); + FileChannel output = fos.getChannel() ) + { + + long size = input.size(); + long pos = 0; + long count; + while ( pos < size ) + { + count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos; + pos += output.transferFrom( input, pos, count ); + } + } + + copyFilePermissions( source, destination ); + } + + /** + * Copy file from source to destination only if source timestamp is later than the destination timestamp. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source An existing non-directory File to copy bytes from. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @return true if no problem occured + * @throws IOException if source does not exist, destination cannot be + * written to, or an IO error occurs during copying. + */ + private static boolean copyFileIfModified( @Nonnull final File source, @Nonnull final File destination ) + throws IOException + { + if ( destination.lastModified() < source.lastModified() ) + { + copyFile( source, destination ); + + return true; + } + + return false; + } + + /** + * Copies bytes from the URL source to a file destination. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source A URL to copy bytes from. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @throws IOException if + *
            + *
          • source URL cannot be opened
          • + *
          • destination cannot be written to
          • + *
          • an IO error occurs during copying
          • + *
          + * @deprecated use {@code java.nio.Files.copy(source.openStream(), destination.toPath(), + * CopyOptions.REPLACE_EXISTING)} + */ + public static void copyURLToFile( @Nonnull final URL source, @Nonnull final File destination ) + throws IOException + { + copyStreamToFile( source.openStream(), destination ); + } + + /** + * Copies bytes from the {@link InputStream} source to a file destination. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source An {@link InputStream} to copy bytes from. This stream is + * guaranteed to be closed. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @throws IOException if + *
            + *
          • source cannot be opened
          • + *
          • destination cannot be written to
          • + *
          • an I/O error occurs during copying
          • + *
          + * @deprecated use {@code java.nio.Files.copy(source, destination.toPath(), + * CopyOptions.REPLACE_EXISTING)} + */ + @Deprecated + private static void copyStreamToFile( @Nonnull @WillClose final InputStream source, + @Nonnull final File destination ) + throws IOException + { + // does destination directory exist ? + if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) + { + // noinspection ResultOfMethodCallIgnored + destination.getParentFile().mkdirs(); + } + + // make sure we can write to destination + if ( destination.exists() && !destination.canWrite() ) + { + final String message = "Unable to open file " + destination + " for writing."; + throw new IOException( message ); + } + + try ( OutputStream out = new FileOutputStream( destination ); InputStream in = source ) + { + IOUtil.copy( in, out ); + } + } + + /** + * Normalize a path. + * Eliminates "/../" and "/./" in a string. Returns null if the ..'s went past the + * root. + * Eg: + *
          +     * /foo//               -->     /foo/
          +     * /foo/./              -->     /foo/
          +     * /foo/../bar          -->     /bar
          +     * /foo/../bar/         -->     /bar/
          +     * /foo/../bar/../baz   -->     /baz
          +     * //foo//./bar         -->     /foo/bar
          +     * /../                 -->     null
          +     * 
          + * + * @param path the path to normalize + * @return the normalized String, or null if too many ..'s. + * @deprecated use {@code org.apache.commons.io.FileNameUtils.normalize()} + */ + @Deprecated + @Nonnull public static String normalize( @Nonnull final String path ) + { + String normalized = path; + // Resolve occurrences of "//" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "//" ); + if ( index < 0 ) + { + break; + } + normalized = normalized.substring( 0, index ) + normalized.substring( index + 1 ); + } + + // Resolve occurrences of "/./" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "/./" ); + if ( index < 0 ) + { + break; + } + normalized = normalized.substring( 0, index ) + normalized.substring( index + 2 ); + } + + // Resolve occurrences of "/../" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "/../" ); + if ( index < 0 ) + { + break; + } + if ( index == 0 ) + { + return null; // Trying to go outside our context + } + int index2 = normalized.lastIndexOf( '/', index - 1 ); + normalized = normalized.substring( 0, index2 ) + normalized.substring( index + 3 ); + } + + // Return the normalized path that we have completed + return normalized; + } + + /** + * Resolve a file filename to it's canonical form. If filename is + * relative (doesn't start with /), it will be resolved relative to + * baseFile, otherwise it is treated as a normal root-relative path. + * + * @param baseFile Where to resolve filename from, if filename is + * relative. + * @param filename absolute or relative file path to resolve + * @return the canonical File of filename + */ + @Nonnull public static File resolveFile( final File baseFile, @Nonnull String filename ) + { + String filenm = filename; + if ( '/' != File.separatorChar ) + { + filenm = filename.replace( '/', File.separatorChar ); + } + + if ( '\\' != File.separatorChar ) + { + filenm = filename.replace( '\\', File.separatorChar ); + } + + // deal with absolute files + if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) ) + { + File file = new File( filenm ); + + try + { + file = file.getCanonicalFile(); + } + catch ( final IOException ioe ) + { + // nop + } + + return file; + } + // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips + // them. However, I'm not sure about this UNC stuff. (JT) + final char[] chars = filename.toCharArray(); + final StringBuilder sb = new StringBuilder(); + + //remove duplicate file separators in succession - except + //on win32 at start of filename as UNC filenames can + //be \\AComputer\AShare\myfile.txt + int start = 0; + if ( '\\' == File.separatorChar ) + { + sb.append( filenm.charAt( 0 ) ); + start++; + } + + for ( int i = start; i < chars.length; i++ ) + { + final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1]; + + if ( !doubleSeparator ) + { + sb.append( chars[i] ); + } + } + + filenm = sb.toString(); + + //must be relative + File file = ( new File( baseFile, filenm ) ).getAbsoluteFile(); + + try + { + file = file.getCanonicalFile(); + } + catch ( final IOException ioe ) + { + // nop + } + + return file; + } + + /** + * Delete a file. If file is directory, delete it and all sub-directories. + * + * @param file the file path + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()} + */ + @Deprecated + public static void forceDelete( @Nonnull final String file ) + throws IOException + { + forceDelete( new File( file ) ); + } + + /** + * Delete a file. If file is directory, delete it and all sub-directories. + * + * @param file a file + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()} + */ + @Deprecated + public static void forceDelete( @Nonnull final File file ) + throws IOException + { + if ( file.isDirectory() ) + { + deleteDirectory( file ); + } + else + { + /* + * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a + * symlink whose target does not exist is deleted, too. + */ + boolean filePresent = file.getCanonicalFile().exists(); + if ( !deleteFile( file ) && filePresent ) + { + final String message = "File " + file + " unable to be deleted."; + throw new IOException( message ); + } + } + } + + /** + * Deletes a file. + * + * @param file the file to delete + * @throws IOException if the file cannot be deleted + * @deprecated use {@code java.nio.files.Files.delete(file.toPath())} + */ + @Deprecated + public static void delete( @Nonnull File file ) + throws IOException + { + Files.delete( file.toPath() ); + } + + /** + * @param file the file + * @return true / false + * @deprecated use {@code java.nio.files.Files.delete(file.toPath())} + */ + @Deprecated + public static boolean deleteLegacyStyle( @Nonnull File file ) + { + try + { + Files.delete( file.toPath() ); + return true; + } + catch ( IOException e ) + { + return false; + } + } + + /** + * Accommodate Windows bug encountered in both Sun and IBM JDKs. + * Others possible. If the delete does not work, call System.gc(), + * wait a little and try again. + * + * @param file a file + * @throws IOException if any + */ + private static boolean deleteFile( @Nonnull File file ) + throws IOException + { + if ( file.isDirectory() ) + { + throw new IOException( "File " + file + " isn't a file." ); + } + + if ( !deleteLegacyStyle( file ) ) + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + file = file.getCanonicalFile(); + } + + try + { + Thread.sleep( 10 ); + return deleteLegacyStyle( file ); + } + catch ( InterruptedException ex ) + { + return deleteLegacyStyle( file ); + } + } + + return true; + } + + + /** + * Make a directory. + * + * @param file not null + * @throws IOException if a file already exists with the specified name or the directory is unable to be created + * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS. + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + */ + public static void forceMkdir( @Nonnull final File file ) + throws IOException + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) + { + throw new IllegalArgumentException( + "The file (" + file.getAbsolutePath() + ") cannot contain any of the following characters: \n" + + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + } + + if ( file.exists() ) + { + if ( file.isFile() ) + { + final String message = + "File " + file + " exists and is " + "not a directory. Unable to create directory."; + throw new IOException( message ); + } + } + else + { + if ( !file.mkdirs() ) + { + final String message = "Unable to create directory " + file; + throw new IOException( message ); + } + } + } + + /** + * Recursively delete a directory. + * + * @param directory a directory + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()} + */ + @Deprecated + public static void deleteDirectory( @Nonnull final String directory ) + throws IOException + { + deleteDirectory( new File( directory ) ); + } + + /** + * Recursively delete a directory. + * + * @param directory a directory + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()} + */ + @Deprecated + public static void deleteDirectory( @Nonnull final File directory ) + throws IOException + { + if ( !directory.exists() ) + { + return; + } + + /* try delete the directory before its contents, which will take + * care of any directories that are really symbolic links. + */ + if ( deleteLegacyStyle( directory ) ) + { + return; + } + + cleanDirectory( directory ); + if ( !deleteLegacyStyle( directory ) ) + { + final String message = "Directory " + directory + " unable to be deleted."; + throw new IOException( message ); + } + } + + /** + * Remove all files from a directory without deleting it. + * + * @param directory a directory + * @throws IOException if any. This can leave cleaning in a half-finished state where + * some but not all files have been deleted. + * @deprecated use {@code org.apache.commons.io.FileUtils.cleanDirectory()} + */ + @Deprecated + public static void cleanDirectory( @Nonnull final File directory ) + throws IOException + { + if ( !directory.exists() ) + { + final String message = directory + " does not exist"; + throw new IllegalArgumentException( message ); + } + + if ( !directory.isDirectory() ) + { + final String message = directory + " is not a directory"; + throw new IllegalArgumentException( message ); + } + + IOException exception = null; + + final File[] files = directory.listFiles(); + + if ( files == null ) + { + return; + } + + for ( final File file : files ) + { + try + { + forceDelete( file ); + } + catch ( final IOException ioe ) + { + exception = ioe; + } + } + + if ( null != exception ) + { + throw exception; + } + } + + /** + * Recursively count size of a directory. + * + * @param directory a directory + * @return size of directory in bytes + * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()} + */ + @Deprecated + public static long sizeOfDirectory( @Nonnull final String directory ) + { + return sizeOfDirectory( new File( directory ) ); + } + + /** + * Recursively count size of a directory. + * + * @param directory a directory + * @return size of directory in bytes + * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()} + */ + @Deprecated + public static long sizeOfDirectory( @Nonnull final File directory ) + { + if ( !directory.exists() ) + { + final String message = directory + " does not exist"; + throw new IllegalArgumentException( message ); + } + + if ( !directory.isDirectory() ) + { + final String message = directory + " is not a directory"; + throw new IllegalArgumentException( message ); + } + + long size = 0; + + final File[] files = directory.listFiles(); + if ( files == null ) + { + throw new IllegalArgumentException( "Problems reading directory" ); + } + + for ( final File file : files ) + { + if ( file.isDirectory() ) + { + size += sizeOfDirectory( file ); + } + else + { + size += file.length(); + } + } + + return size; + } + + /** + * Return the files contained in the directory, using inclusion and exclusion Ant patterns, + * including the directory name in each of the files + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @return a list of File objects + * @throws IOException in case of failure. + * @see #getFileNames(File, String, String, boolean) + */ + @Nonnull + public static List getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes ) + throws IOException + { + return getFiles( directory, includes, excludes, true ); + } + + /** + * Return the files contained in the directory, using inclusion and exclusion Ant patterns + * + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each file + * @return a list of File objects + * @throws IOException in case of failure. + * @see #getFileNames(File, String, String, boolean) + */ + @Nonnull + public static List getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, + boolean includeBasedir ) + throws IOException + { + List fileNames = getFileNames( directory, includes, excludes, includeBasedir ); + + List files = new ArrayList(); + + for ( String filename : fileNames ) + { + files.add( new File( filename ) ); + } + + return files; + } + + /** + * Return a list of files as String depending options. + * This method use case sensitive file name. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @return a list of file names + * @throws IOException in case of failure + */ + @Nonnull public static List getFileNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir ) + throws IOException + { + return getFileNames( directory, includes, excludes, includeBasedir, true ); + } + + /** + * Return a list of files as String depending options. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each String of file + * @param isCaseSensitive true if case sensitive + * @return a list of files as String + * @throws IOException + */ + @Nonnull private static List getFileNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive ) + throws IOException + { + return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false ); + } + + /** + * Return a list of directories as String depending options. + * This method use case sensitive file name. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each String of file + * @return a list of directories as String + * @throws IOException in case of failure. + */ + @Nonnull public static List getDirectoryNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir ) + throws IOException + { + return getDirectoryNames( directory, includes, excludes, includeBasedir, true ); + } + + /** + * Return a list of directories as Strings. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @param isCaseSensitive true if case sensitive + * @return a list of directories as String + * @throws IOException in case of failure + */ + @Nonnull public static List getDirectoryNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive ) + throws IOException + { + return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true ); + } + + /** + * Return a list of file names as Strings. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @param isCaseSensitive true if case sensitive + * @param getFiles true to include regular files + * @param getDirectories true to include directories + * @return a list of file names + */ +/** + * Return a list of file names as Strings. + * + * @param directory + * the directory to scan + * @param includes + * the Ant includes pattern, comma separated + * @param excludes + * the Ant excludes pattern, comma separated + * @param includeBasedir + * true to include the base directory in each String of file + * @param isCaseSensitive + * true if case sensitive + * @param getFiles + * true to include regular files + * @param getDirectories + * true to include directories + * @return a list of file names + */ +public static java.util.List getFileAndDirectoryNames(java.io.File directory, @javax.annotation.Nullable +java.lang.String includes, @javax.annotation.Nullable +java.lang.String excludes, boolean includeBasedir, boolean isCaseSensitive, boolean getFiles, boolean getDirectories) { + org.apache.maven.shared.utils.io.DirectoryScanner scanner = new org.apache.maven.shared.utils.io.DirectoryScanner(); + scanner.setBasedir(directory); + { + scanner.setIncludes(org.apache.maven.shared.utils.StringUtils.split(/* NPEX_NULL_EXP */ + includes, ",")); + } + if (excludes != null) { + scanner.setExcludes(org.apache.maven.shared.utils.StringUtils.split(excludes, ",")); + } + scanner.setCaseSensitive(isCaseSensitive); + scanner.scan(); + java.util.List list = new java.util.ArrayList(); + if (getFiles) { + java.lang.String[] files = scanner.getIncludedFiles(); + for (java.lang.String file : files) { + if (includeBasedir) { + list.add((directory + org.apache.maven.shared.utils.io.FileUtils.FS) + file); + } else { + list.add(file); + } + } + } + if (getDirectories) { + java.lang.String[] directories = scanner.getIncludedDirectories(); + for (java.lang.String directory1 : directories) { + if (includeBasedir) { + list.add((directory + org.apache.maven.shared.utils.io.FileUtils.FS) + directory1); + } else { + list.add(directory1); + } + } + } + return list; +} + + /** + * Copy the contents of a directory into another one. + * + * @param sourceDirectory the source directory + * @param destinationDirectory the target directory + * @throws IOException if any + */ + public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) + throws IOException + { + copyDirectory( sourceDirectory, destinationDirectory, "**", null ); + } + + /** + * Copy the contents of a directory into another one. + * + * @param sourceDirectory the source directory + * @param destinationDirectory the target directory + * @param includes Ant include pattern + * @param excludes Ant exclude pattern + * @throws IOException if any + * @see #getFiles(File, String, String) + */ + public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, + @Nullable String includes, @Nullable String excludes ) + throws IOException + { + if ( !sourceDirectory.exists() ) + { + return; + } + + List files = getFiles( sourceDirectory, includes, excludes ); + + for ( File file : files ) + { + copyFileToDirectory( file, destinationDirectory ); + } + } + + /** + * Copies an entire directory structure. + *

          + * Note: + *

            + *
          • It will include empty directories. + *
          • The sourceDirectory must exist. + *
          + * + * @param sourceDirectory the source dir + * @param destinationDirectory the target dir + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.copyDirectory()} + */ + @Deprecated + public static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) + throws IOException + { + copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, false ); + } + + private static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, + File rootDestinationDirectory, boolean onlyModifiedFiles ) + throws IOException + { + //noinspection ConstantConditions + if ( sourceDirectory == null ) + { + throw new IOException( "source directory can't be null." ); + } + + //noinspection ConstantConditions + if ( destinationDirectory == null ) + { + throw new IOException( "destination directory can't be null." ); + } + + if ( sourceDirectory.equals( destinationDirectory ) ) + { + throw new IOException( "source and destination are the same directory." ); + } + + if ( !sourceDirectory.exists() ) + { + throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); + } + + File[] files = sourceDirectory.listFiles(); + + if ( files == null ) + { + return; + } + + String sourcePath = sourceDirectory.getAbsolutePath(); + + for ( File file : files ) + { + if ( file.equals( rootDestinationDirectory ) ) + { + // We don't copy the destination directory in itself + continue; + } + + String dest = file.getAbsolutePath(); + + dest = dest.substring( sourcePath.length() + 1 ); + + File destination = new File( destinationDirectory, dest ); + + if ( file.isFile() ) + { + destination = destination.getParentFile(); + + if ( onlyModifiedFiles ) + { + copyFileToDirectoryIfModified( file, destination ); + } + else + { + copyFileToDirectory( file, destination ); + } + } + else if ( file.isDirectory() ) + { + if ( !destination.exists() && !destination.mkdirs() ) + { + throw new IOException( + "Could not create destination directory '" + destination.getAbsolutePath() + "'." ); + } + + copyDirectoryStructure( file, destination, rootDestinationDirectory, onlyModifiedFiles ); + } + else + { + throw new IOException( "Unknown file type: " + file.getAbsolutePath() ); + } + } + } + + /** + * Renames a file, even if that involves crossing file system boundaries. + *

          + *

          This will remove to (if it exists), ensure that + * to's parent directory exists and move + * from, which involves deleting from as + * well.

          + * + * @param from the file to move + * @param to the new file name + * @throws IOException if anything bad happens during this process. + * Note that to may have been deleted already when this happens. + * @deprecated use {@code java.nio.Files.move()} + */ + @Deprecated + public static void rename( @Nonnull File from, @Nonnull File to ) + throws IOException + { + if ( to.exists() && !deleteLegacyStyle( to ) ) + { + throw new IOException( "Failed to delete " + to + " while trying to rename " + from ); + } + + File parent = to.getParentFile(); + if ( parent != null && !parent.exists() && !parent.mkdirs() ) + { + throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from ); + } + + if ( !from.renameTo( to ) ) + { + copyFile( from, to ); + if ( !deleteLegacyStyle( from ) ) + { + throw new IOException( "Failed to delete " + from + " while trying to rename it." ); + } + } + } + + /** + * Create a temporary file in a given directory. + *

          + *

          The file denoted by the returned abstract pathname did not + * exist before this method was invoked, any subsequent invocation + * of this method will yield a different file name.

          + *

          + * The filename is prefixNNNNNsuffix where NNNN is a random number + *

          + *

          This method is different to {@link File#createTempFile(String, String, File)} + * as it doesn't create the file itself. + * It uses the location pointed to by java.io.tmpdir + * when the parentDir attribute is null.

          + *

          To automatically delete the file created by this method, use the + * {@link File#deleteOnExit()} method.

          + * + * @param prefix prefix before the random number + * @param suffix file extension; include the '.' + * @param parentDir directory to create the temporary file in -java.io.tmpdir + * used if not specified + * @return a File reference to the new temporary file. + * @deprecated use {@code java.nio.Files.createTempFile()} + */ + @Deprecated + public static File createTempFile( @Nonnull String prefix, @Nonnull String suffix, @Nullable File parentDir ) + { + File result; + String parent = System.getProperty( "java.io.tmpdir" ); + if ( parentDir != null ) + { + parent = parentDir.getPath(); + } + DecimalFormat fmt = new DecimalFormat( "#####" ); + SecureRandom secureRandom = new SecureRandom(); + long secureInitializer = secureRandom.nextLong(); + Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() ); + do + { + result = new File( parent, prefix + fmt.format( positiveRandom( rand ) ) + suffix ); + } + while ( result.exists() ); + + return result; + } + + private static int positiveRandom( Random rand ) + { + int a = rand.nextInt(); + while ( a == Integer.MIN_VALUE ) + { + a = rand.nextInt(); + } + return Math.abs( a ); + } + + /** + * If wrappers is null or empty, the file will be copied only if to.lastModified() < from.lastModified() + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} + * @throws IOException if an IO error occurs during copying or filtering + */ + public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, + @Nullable FilterWrapper... wrappers ) + throws IOException + { + copyFile( from, to, encoding, wrappers, false ); + } + + /** + * Wrapper class for Filter. + */ + public abstract static class FilterWrapper + { + /** + * @param fileReader {@link Reader} + * @return the Reader instance + */ + public abstract Reader getReader( Reader fileReader ); + } + + /** + * If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if + * overwrite is true + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} + * @param overwrite if true and wrappers is null or empty, the file will be copied even if + * to.lastModified() < from.lastModified() + * @throws IOException if an IO error occurs during copying or filtering + */ + public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, + @Nullable FilterWrapper[] wrappers, boolean overwrite ) + throws IOException + { + if ( wrappers == null || wrappers.length == 0 ) + { + if ( overwrite || to.lastModified() < from.lastModified() ) + { + copyFile( from, to ); + } + } + else + { + Charset charset = charset( encoding ); + + // buffer so it isn't reading a byte at a time! + try ( Reader fileReader = Files.newBufferedReader( from.toPath(), charset ) ) + { + Reader wrapped = fileReader; + for ( FilterWrapper wrapper : wrappers ) + { + wrapped = wrapper.getReader( wrapped ); + } + + if ( overwrite || !to.exists() ) + { + try ( Writer fileWriter = Files.newBufferedWriter( to.toPath(), charset ) ) + { + IOUtil.copy( wrapped, fileWriter ); + } + } + else + { + CharsetEncoder encoder = charset.newEncoder(); + + int totalBufferSize = FILE_COPY_BUFFER_SIZE; + + int charBufferSize = ( int ) Math.floor( totalBufferSize / ( 2 + 2 * encoder.maxBytesPerChar() ) ); + int byteBufferSize = ( int ) Math.ceil( charBufferSize * encoder.maxBytesPerChar() ); + + CharBuffer newChars = CharBuffer.allocate( charBufferSize ); + ByteBuffer newBytes = ByteBuffer.allocate( byteBufferSize ); + ByteBuffer existingBytes = ByteBuffer.allocate( byteBufferSize ); + + CoderResult coderResult; + int existingRead; + boolean writing = false; + + try ( final RandomAccessFile existing = new RandomAccessFile( to, "rw" ) ) + { + int n; + while ( -1 != ( n = wrapped.read( newChars ) ) ) + { + ( ( Buffer ) newChars ).flip(); + + coderResult = encoder.encode( newChars, newBytes, n != 0 ); + if ( coderResult.isError() ) + { + coderResult.throwException(); + } + + ( ( Buffer ) newBytes ).flip(); + + if ( !writing ) + { + existingRead = existing.read( existingBytes.array(), 0, newBytes.remaining() ); + ( ( Buffer ) existingBytes ).position( existingRead ); + ( ( Buffer ) existingBytes ).flip(); + + if ( newBytes.compareTo( existingBytes ) != 0 ) + { + writing = true; + if ( existingRead > 0 ) + { + existing.seek( existing.getFilePointer() - existingRead ); + } + } + } + + if ( writing ) + { + existing.write( newBytes.array(), 0, newBytes.remaining() ); + } + + ( ( Buffer ) newChars ).clear(); + ( ( Buffer ) newBytes ).clear(); + ( ( Buffer ) existingBytes ).clear(); + } + + if ( existing.length() > existing.getFilePointer() ) + { + existing.setLength( existing.getFilePointer() ); + } + } + } + } + } + + copyFilePermissions( from, to ); + } + + /** + * Attempts to copy file permissions from the source to the destination file. + * Initially attempts to copy posix file permissions, assuming that the files are both on posix filesystems. + * If the initial attempts fail then a second attempt using less precise permissions model. + * Note that permissions are copied on a best-efforts basis, + * failure to copy permissions will not result in an exception. + * + * @param source the file to copy permissions from. + * @param destination the file to copy permissions to. + */ + private static void copyFilePermissions( @Nonnull File source, @Nonnull File destination ) + throws IOException + { + try + { + // attempt to copy posix file permissions + Files.setPosixFilePermissions( + destination.toPath(), + Files.getPosixFilePermissions( source.toPath() ) + ); + } + catch ( UnsupportedOperationException e ) + { + // fallback to setting partial permissions + destination.setExecutable( source.canExecute() ); + destination.setReadable( source.canRead() ); + destination.setWritable( source.canWrite() ); + } + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file + * @return a List containing every every line not starting with # and not empty + * @throws IOException if any + * @deprecated assumes the platform default character set + */ + @Deprecated + @Nonnull public static List loadFile( @Nonnull File file ) + throws IOException + { + List lines = new ArrayList(); + + if ( file.exists() ) + { + try ( BufferedReader reader = Files.newBufferedReader( file.toPath(), Charset.defaultCharset() ) ) + { + for ( String line = reader.readLine(); line != null; line = reader.readLine() ) + { + line = line.trim(); + if ( !line.startsWith( "#" ) && line.length() != 0 ) + { + lines.add( line ); + } + } + } + } + + return lines; + + } + + /** + * Returns the named charset or the default charset. + * @param encoding the name or alias of the charset, null or empty + * @return A charset object for the named or default charset. + */ + private static Charset charset( String encoding ) + { + if ( encoding == null || encoding.isEmpty() ) + { + return Charset.defaultCharset(); + } + else + { + return Charset.forName( encoding ); + } + } + + /** + * For Windows OS, check if the file name contains any of the following characters: + * ":", "*", "?", "\"", "<", ">", "|" + * + * @param f not null file + * @return false if the file path contains any of forbidden Windows characters, + * true if the Os is not Windows or if the file path respect the Windows constraints. + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + */ + private static boolean isValidWindowsFileName( @Nonnull File f ) + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 ) + { + return false; + } + + if ( f.getParentFile() != null ) + { + return isValidWindowsFileName( f.getParentFile() ); + } + } + + return true; + } + + /** + * Checks whether a given file is a symbolic link. + * + * @param file the file to check + * @throws IOException in case of failure. + * @return true if symbolic link false otherwise. + * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())} + */ + @Deprecated + public static boolean isSymbolicLink( @Nonnull final File file ) + throws IOException + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * Checks whether a given file is a symbolic link. + * + * @param file the file to check + * @return true if and only if we reliably can say this is a symlink + * + * @throws IOException in case of failure + * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())} + */ + @Deprecated + public static boolean isSymbolicLinkForSure( @Nonnull final File file ) + throws IOException + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * Create a new symbolic link, possibly replacing an existing symbolic link. + * + * @param symlink the link name + * @param target the target + * @return the linked file + * @throws IOException in case of an error + * @see {@code java.nio.file.Files.createSymbolicLink(Path)} which creates a new + * symbolic link but does not replace exsiting symbolic links + */ + @Nonnull public static File createSymbolicLink( @Nonnull File symlink, @Nonnull File target ) + throws IOException + { + final Path symlinkPath = symlink.toPath(); + + if ( Files.exists( symlinkPath ) ) + { + if ( target.equals( Files.readSymbolicLink( symlinkPath ).toFile() ) ) + { + return symlink; + } + + Files.delete( symlinkPath ); + } + + return Files.createSymbolicLink( symlinkPath, target.toPath() ).toFile(); + } +} diff --git a/Java/maven-shared-utils-FileUtils_1553/metadata.json b/Java/maven-shared-utils-FileUtils_1553/metadata.json new file mode 100644 index 000000000..020004fb1 --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1553/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-FileUtils_1553", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/FileUtils.java", + "line": 1570, + "npe_method": "getFileAndDirectoryNames", + "deref_field": "includes", + "npe_class": "FileUtils", + "repo": "maven-shared-utils", + "bug_id": "FileUtils_1553" + } +} diff --git a/Java/maven-shared-utils-FileUtils_1553/npe.json b/Java/maven-shared-utils-FileUtils_1553/npe.json new file mode 100644 index 000000000..aa5c04eea --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1553/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/FileUtils.java", + "line": 1570, + "npe_method": "getFileAndDirectoryNames", + "deref_field": "includes", + "npe_class": "FileUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-FileUtils_1558/Dockerfile b/Java/maven-shared-utils-FileUtils_1558/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1558/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-FileUtils_1558/buggy.java b/Java/maven-shared-utils-FileUtils_1558/buggy.java new file mode 100644 index 000000000..5795f5b91 --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1558/buggy.java @@ -0,0 +1,2141 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import org.apache.maven.shared.utils.Os; +import org.apache.maven.shared.utils.StringUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.WillClose; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * This class provides basic facilities for manipulating files and file paths. + *

          + *

          Path-related methods

          + *

          + *

          Methods exist to retrieve the components of a typical file path. For example + * /www/hosted/mysite/index.html, can be broken into: + *

            + *
          • /www/hosted/mysite/index -- retrievable through {@link #removeExtension}
          • + *
          • html -- retrievable through {@link #getExtension}
          • + *
          + *

          + *

          + *

          File-related methods

          + *

          + * There are methods to create a {@link #toFile File from a URL}, copy a + * copy a {@link #copyFile File to another File}, + * copy a {@link #copyURLToFile URL's contents to a File}, + * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File) + * clean} a directory. + *

          + *

          + * Common {@link java.io.File} manipulation routines. + *

          + * Taken from the commons-utils repo. + * Also code from Alexandria's FileUtils. + * And from Avalon Excalibur's IO. + * And from Ant. + * + * @author Kevin A. Burton + * @author Scott Sanders + * @author Daniel Rall + * @author Christoph.Reck + * @author Peter Donald + * @author Jeff Turner + * + */ +public class FileUtils +{ + /** + * protected constructor. + */ + protected FileUtils() + { + // This is a utility class. Normally don't instantiate + } + + /** + * The number of bytes in a kilobyte. + */ + private static final int ONE_KB = 1024; + + /** + * The number of bytes in a megabyte. + */ + private static final int ONE_MB = ONE_KB * ONE_KB; + + /** + * The file copy buffer size (30 MB) + */ + private static final int FILE_COPY_BUFFER_SIZE = ONE_MB * 30; + + /** + * The vm line separator + */ + private static final String FS = System.getProperty( "file.separator" ); + + /** + * Non-valid Characters for naming files, folders under Windows: ":", "*", "?", "\"", "<", ">", "|" + * + * @see + * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13 + */ + private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" }; + + /** + * @return the default excludes pattern + * @see DirectoryScanner#DEFAULTEXCLUDES + */ + @Nonnull public static String[] getDefaultExcludes() + { + return DirectoryScanner.DEFAULTEXCLUDES; + } + + /** + * @return the default excludes pattern as list + * @see #getDefaultExcludes() + */ + @Nonnull public static List getDefaultExcludesAsList() + { + return Arrays.asList( getDefaultExcludes() ); + } + + /** + * @return the default excludes pattern as comma separated string. + * @see DirectoryScanner#DEFAULTEXCLUDES + * @see StringUtils#join(Object[], String) + */ + @Nonnull public static String getDefaultExcludesAsString() + { + return StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, "," ); + } + + /** + * Returns the directory path portion of a file specification string. + * Matches the equally named unix command. + * + * @param path the file path + * @return the directory portion excluding the ending file separator + * @deprecated use {@code Paths.get(path).getParent().getName()} + */ + @Deprecated + @Nonnull public static String dirname( @Nonnull String path ) + { + int i = path.lastIndexOf( File.separator ); + return ( i >= 0 ? path.substring( 0, i ) : "" ); + } + + /** + * Returns the filename portion of a path. + * + * @param path the file path + * @return the filename string with extension + * @deprecated use {@code Paths.get(path).getName()} + */ + @Deprecated + @Nonnull public static String filename( @Nonnull String path ) + { + int i = path.lastIndexOf( File.separator ); + return ( i >= 0 ? path.substring( i + 1 ) : path ); + } + + /** + * Returns the extension portion of a file path. + * This is everything after the last dot '.' in the path (NOT including the dot). + * + * @param path the file path + * @return the extension of the file + * @deprecated use {@code org.apache.commons.io.FilenameUtils.getExtension} + */ + @Deprecated + @Nonnull public static String extension( @Nonnull String path ) + { + // Ensure the last dot is after the last file separator + int lastSep = path.lastIndexOf( File.separatorChar ); + int lastDot; + if ( lastSep < 0 ) + { + lastDot = path.lastIndexOf( '.' ); + } + else + { + lastDot = path.substring( lastSep + 1 ).lastIndexOf( '.' ); + if ( lastDot >= 0 ) + { + lastDot += lastSep + 1; + } + } + + if ( lastDot >= 0 && lastDot > lastSep ) + { + return path.substring( lastDot + 1 ); + } + + return ""; + } + + /** + * Check if a file exists. + * + * @param fileName the file path + * @return true if file exists + * @deprecated use {@code java.io.File.exists()} + */ + @Deprecated + public static boolean fileExists( @Nonnull String fileName ) + { + File file = new File( fileName ); + return file.exists(); + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file path + * @return the file content using the platform encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file))} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull String file ) + throws IOException + { + return fileRead( file, null ); + } + + /** + * @param file the file path + * @param encoding the wanted encoding + * @return the file content using the specified encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(Paths.get(file)), encoding)} + */ + @Deprecated + @Nonnull private static String fileRead( @Nonnull String file, @Nullable String encoding ) + throws IOException + { + return fileRead( new File( file ), encoding ); + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file path + * @return the file content using the platform encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file.toPath()))} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull File file ) + throws IOException + { + return fileRead( file, null ); + } + + /** + * @param file the file path + * @param encoding the wanted encoding + * @return the file content using the specified encoding + * @throws IOException if any + * @deprecated use {@code new String(java.nio.files.Files.readAllBytes(file.toPath()), encoding)} + */ + @Deprecated + @Nonnull public static String fileRead( @Nonnull File file, @Nullable String encoding ) + throws IOException + { + Charset charset = charset( encoding ); + + StringBuilder buf = new StringBuilder(); + + + try ( Reader reader = Files.newBufferedReader( file.toPath(), charset ) ) + { + int count; + char[] b = new char[512]; + while ( ( count = reader.read( b ) ) >= 0 ) // blocking read + { + buf.append( b, 0, count ); + } + } + + return buf.toString(); + } + + /** + * @param file the file path + * @return the file content lines as String[] using the system default encoding. + * An empty List if the file doesn't exist. + * @throws IOException in case of failure + * @deprecated use {@code java.nio.files.Files.readAllLines()} + */ + @Deprecated + @Nonnull public static String[] fileReadArray( @Nonnull File file ) + throws IOException + { + List lines = loadFile( file ); + + return lines.toArray( new String[lines.size()] ); + } + + /** + * Appends data to a file. The file is created if it does not exist. + * Note: the data is written with platform encoding. + * + * @param fileName the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(), + * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileAppend( @Nonnull String fileName, @Nonnull String data ) + throws IOException + { + fileAppend( fileName, null, data ); + } + + /** + * Appends data to a file. The file will be created if it does not exist. + * + * @param fileName the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(encoding), + * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileAppend( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( OutputStream out = new FileOutputStream( fileName, true ) ) + { + out.write( data.getBytes( charset ) ); + } + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * Note: the data is written with platform encoding + * + * @param fileName the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(filename, + * data.getBytes(), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull String fileName, @Nonnull String data ) + throws IOException + { + fileWrite( fileName, null, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param fileName the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(Paths.get(filename), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + File file = new File( fileName ); + fileWrite( file, encoding, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param file the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWrite( @Nonnull File file, @Nullable String encoding, @Nonnull String data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) ) + { + writer.write( data ); + } + } + + /** + * Writes String array data to a file in the systems default encoding. + * The file will be created if it does not exist. + * + * @param file the path of the file to write + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWriteArray( @Nonnull File file, @Nullable String... data ) + throws IOException + { + fileWriteArray( file, null, data ); + } + + /** + * Writes String array data to a file. The file is created if it does not exist. + * + * @param file the path of the file to write + * @param encoding the encoding of the file + * @param data the content to write to the file + * @throws IOException if any + * @deprecated use {@code java.nio.files.Files.write(file.toPath(), + * data.getBytes(encoding), StandardOpenOption.CREATE)} + */ + @Deprecated + public static void fileWriteArray( @Nonnull File file, @Nullable String encoding, @Nullable String... data ) + throws IOException + { + Charset charset = charset( encoding ); + + try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) ) + { + for ( int i = 0; data != null && i < data.length; i++ ) + { + writer.write( data[i] ); + if ( i < data.length ) + { + writer.write( "\n" ); + } + } + } + } + + /** + * Deletes a file. + * + * @param fileName the path of the file to delete + * @deprecated use {@code Files.delete(Paths.get(fileName))} + */ + @Deprecated + public static void fileDelete( @Nonnull String fileName ) + { + File file = new File( fileName ); + //noinspection ResultOfMethodCallIgnored + deleteLegacyStyle( file ); + } + + /** + * Given a directory and an array of extensions return an array of compliant files. + *

          + * The given extensions should be like "java" and not like ".java". + * + * @param directory the path of the directory + * @param extensions an array of expected extensions + * @return an array of files for the wanted extensions + */ + public static String[] getFilesFromExtension( @Nonnull String directory, @Nonnull String... extensions ) + { + List files = new ArrayList(); + + File currentDir = new File( directory ); + + String[] unknownFiles = currentDir.list(); + + if ( unknownFiles == null ) + { + return new String[0]; + } + + for ( String unknownFile : unknownFiles ) + { + String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFile; + File currentFile = new File( currentFileName ); + + if ( currentFile.isDirectory() ) + { + // ignore all CVS directories... + if ( currentFile.getName().equals( "CVS" ) ) + { + continue; + } + + // ok... traverse into this directory and get all the files... then combine + // them with the current list. + + String[] fetchFiles = getFilesFromExtension( currentFileName, extensions ); + files = blendFilesToList( files, fetchFiles ); + } + else + { + // ok... add the file + + String add = currentFile.getAbsolutePath(); + if ( isValidFile( add, extensions ) ) + { + files.add( add ); + } + } + } + + // ok... move the Vector into the files list... + String[] foundFiles = new String[files.size()]; + files.toArray( foundFiles ); + + return foundFiles; + } + + /** + * Private helper method for getFilesFromExtension() + */ + @Nonnull private static List blendFilesToList( @Nonnull List v, @Nonnull String... files ) + { + Collections.addAll( v, files ); + + return v; + } + + /** + * Checks to see if a file is of a particular type(s). + * Note that if the file does not have an extension, an empty string + * ("") is matched for. + */ + private static boolean isValidFile( @Nonnull String file, @Nonnull String... extensions ) + { + String extension = extension( file ); + + // ok.. now that we have the "extension" go through the current know + // excepted extensions and determine if this one is OK. + + for ( String extension1 : extensions ) + { + if ( extension1.equals( extension ) ) + { + return true; + } + } + + return false; + + } + + /** + * Simple way to make a directory. + * + * @param dir the directory to create + * @throws IllegalArgumentException if the dir contains illegal Windows characters under Windows OS + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + * @deprecated use {@code java.nio.file.Files.createDirectories(Paths.get(dir))} + */ + @Deprecated + public static void mkdir( @Nonnull String dir ) + { + File file = new File( dir ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) + { + throw new IllegalArgumentException( + "The file (" + dir + ") cannot contain any of the following characters: \n" + StringUtils.join( + INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + } + + if ( !file.exists() ) + { + //noinspection ResultOfMethodCallIgnored + file.mkdirs(); + } + } + + /** + * Compare the contents of two files to determine if they are equal or not. + * + * @param file1 the first file + * @param file2 the second file + * @return true if the content of the files are equal or they both don't exist, false otherwise + * @throws IOException if any + */ + public static boolean contentEquals( @Nonnull final File file1, @Nonnull final File file2 ) + throws IOException + { + final boolean file1Exists = file1.exists(); + if ( file1Exists != file2.exists() ) + { + return false; + } + + if ( !file1Exists ) + { + // two not existing files are equal + return true; + } + + if ( file1.isDirectory() || file2.isDirectory() ) + { + // don't want to compare directory contents + return false; + } + + try ( InputStream input1 = new FileInputStream( file1 ); + InputStream input2 = new FileInputStream( file2 ) ) + { + return IOUtil.contentEquals( input1, input2 ); + } + } + + /** + * Convert from a URL to a File. + * + * @param url file URL + * @return the equivalent File object, or null if the URL's protocol + * is not file + */ + @Nullable public static File toFile( @Nullable final URL url ) + { + if ( url == null || !url.getProtocol().equalsIgnoreCase( "file" ) ) + { + return null; + } + + String filename = url.getFile().replace( '/', File.separatorChar ); + int pos = -1; + while ( ( pos = filename.indexOf( '%', pos + 1 ) ) >= 0 ) + { + if ( pos + 2 < filename.length() ) + { + String hexStr = filename.substring( pos + 1, pos + 3 ); + char ch = (char) Integer.parseInt( hexStr, 16 ); + filename = filename.substring( 0, pos ) + ch + filename.substring( pos + 3 ); + } + } + return new File( filename ); + } + + /** + * Convert the array of Files into a list of URLs. + * + * @param files the array of files + * @return the array of URLs + * @throws IOException if an error occurs + */ + @Nonnull public static URL[] toURLs( @Nonnull final File... files ) + throws IOException + { + final URL[] urls = new URL[files.length]; + + for ( int i = 0; i < urls.length; i++ ) + { + urls[i] = files[i].toURI().toURL(); + } + + return urls; + } + + /** + * Remove extension from a path. E.g. + *

          +     * foo.txt    --> foo
          +     * a\b\c.jpg --> a\b\c
          +     * a\b\c     --> a\b\c
          +     * 
          + * + * @param filename the path of the file + * @return the filename minus extension + * @deprecated use {@code org.apache.commons.io.FilenameUtils.removeExtension()} + */ + @Deprecated + @Nonnull public static String removeExtension( @Nonnull final String filename ) + { + String ext = extension( filename ); + + if ( "".equals( ext ) ) + { + return filename; + } + + final int index = filename.lastIndexOf( ext ) - 1; + return filename.substring( 0, index ); + } + + /** + * Get extension from a path. E.g. + * + *
          +     * foo.txt    --> "txt"
          +     * a\b\c.jpg --> "jpg"
          +     * a\b\c     --> ""
          +     * 
          + * + * @param filename the path of the file + * @return the extension of filename or "" if none + * @deprecated use {@code org.apache.commons.io.FilenameUtils.getExtension()} + */ + @Deprecated + @Nonnull public static String getExtension( @Nonnull final String filename ) + { + return extension( filename ); + } + + /** + * Copy file from source to destination. If destinationDirectory does not exist, it + * (and any parent directories) will be created. If a file source in + * destinationDirectory exists, it will be overwritten. + * + * @param source an existing File to copy + * @param destinationDirectory a directory to copy source into + * @throws java.io.FileNotFoundException if source isn't a normal file + * @throws IllegalArgumentException if destinationDirectory isn't a directory + * @throws IOException if source does not exist, the file in + * destinationDirectory cannot be written to, or an IO error + * occurs during copying. + * @deprecated use {@code org.apache.commons.io.FileUtils.copyFileToDirectory()} + */ + @Deprecated + public static void copyFileToDirectory( @Nonnull final File source, @Nonnull final File destinationDirectory ) + throws IOException + { + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFile( source, new File( destinationDirectory, source.getName() ) ); + } + + /** + * Copy file from source to destination only if source is newer than the target file. + * If destinationDirectory does not exist, it + * (and any parent directories) is created. If a file source in + * destinationDirectory exists, it is overwritten. + * + * @param source an existing File to copy + * @param destinationDirectory a directory to copy source into + * @throws java.io.FileNotFoundException if source isn't a normal file + * @throws IllegalArgumentException if destinationDirectory isn't a directory + * @throws IOException if source does not exist, the file in + * destinationDirectory cannot be written to, or an IO error + * occurs during copying + */ + private static void copyFileToDirectoryIfModified( @Nonnull final File source, + @Nonnull final File destinationDirectory ) + throws IOException + { + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFileIfModified( source, new File( destinationDirectory, source.getName() ) ); + } + + + /** + * Copy file from source to destination. The directories up to destination will be + * created if they don't already exist. destination will be overwritten if it + * already exists. + * + * @param source an existing non-directory File to copy bytes from + * @param destination a non-directory File to write bytes to (possibly + * overwriting) + * @throws IOException if source does not exist, destination cannot be + * written to, or an IO error occurs during copying + * @throws java.io.FileNotFoundException if destination is a directory + * @deprecated use {@code java.nio.Files.copy(source.toPath(), destination.toPath(), CopyOptions.NOFOLLOW_LINKS, + * CopyOptions.REPLACE_EXISTING)} + */ + @Deprecated + public static void copyFile( @Nonnull final File source, @Nonnull final File destination ) + throws IOException + { + //check source exists + if ( !source.exists() ) + { + final String message = "File " + source + " does not exist"; + throw new IOException( message ); + } + if ( Files.isSymbolicLink( source.toPath() ) ) + { + File target = Files.readSymbolicLink( source.toPath() ).toFile(); + createSymbolicLink( destination, target ); + return; + } + + //check source != destination, see PLXUTILS-10 + if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) ) + { + //if they are equal, we can exit the method without doing any work + return; + } + + mkdirsFor( destination ); + + doCopyFile( source, destination ); + + if ( source.length() != destination.length() ) + { + final String message = "Failed to copy full contents from " + source + " to " + destination; + throw new IOException( message ); + } + } + + private static void mkdirsFor( @Nonnull File destination ) + { + //does destination directory exist ? + if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) + { + //noinspection ResultOfMethodCallIgnored + destination.getParentFile().mkdirs(); + } + } + + private static void doCopyFile( @Nonnull File source, @Nonnull File destination ) + throws IOException + { + + try ( FileInputStream fis = new FileInputStream( source ); + FileOutputStream fos = new FileOutputStream( destination ); + FileChannel input = fis.getChannel(); + FileChannel output = fos.getChannel() ) + { + + long size = input.size(); + long pos = 0; + long count; + while ( pos < size ) + { + count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos; + pos += output.transferFrom( input, pos, count ); + } + } + + copyFilePermissions( source, destination ); + } + + /** + * Copy file from source to destination only if source timestamp is later than the destination timestamp. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source An existing non-directory File to copy bytes from. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @return true if no problem occured + * @throws IOException if source does not exist, destination cannot be + * written to, or an IO error occurs during copying. + */ + private static boolean copyFileIfModified( @Nonnull final File source, @Nonnull final File destination ) + throws IOException + { + if ( destination.lastModified() < source.lastModified() ) + { + copyFile( source, destination ); + + return true; + } + + return false; + } + + /** + * Copies bytes from the URL source to a file destination. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source A URL to copy bytes from. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @throws IOException if + *
            + *
          • source URL cannot be opened
          • + *
          • destination cannot be written to
          • + *
          • an IO error occurs during copying
          • + *
          + * @deprecated use {@code java.nio.Files.copy(source.openStream(), destination.toPath(), + * CopyOptions.REPLACE_EXISTING)} + */ + public static void copyURLToFile( @Nonnull final URL source, @Nonnull final File destination ) + throws IOException + { + copyStreamToFile( source.openStream(), destination ); + } + + /** + * Copies bytes from the {@link InputStream} source to a file destination. + * The directories up to destination will be created if they don't already exist. + * destination will be overwritten if it already exists. + * + * @param source An {@link InputStream} to copy bytes from. This stream is + * guaranteed to be closed. + * @param destination A non-directory File to write bytes to (possibly + * overwriting). + * @throws IOException if + *
            + *
          • source cannot be opened
          • + *
          • destination cannot be written to
          • + *
          • an I/O error occurs during copying
          • + *
          + * @deprecated use {@code java.nio.Files.copy(source, destination.toPath(), + * CopyOptions.REPLACE_EXISTING)} + */ + @Deprecated + private static void copyStreamToFile( @Nonnull @WillClose final InputStream source, + @Nonnull final File destination ) + throws IOException + { + // does destination directory exist ? + if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) + { + // noinspection ResultOfMethodCallIgnored + destination.getParentFile().mkdirs(); + } + + // make sure we can write to destination + if ( destination.exists() && !destination.canWrite() ) + { + final String message = "Unable to open file " + destination + " for writing."; + throw new IOException( message ); + } + + try ( OutputStream out = new FileOutputStream( destination ); InputStream in = source ) + { + IOUtil.copy( in, out ); + } + } + + /** + * Normalize a path. + * Eliminates "/../" and "/./" in a string. Returns null if the ..'s went past the + * root. + * Eg: + *
          +     * /foo//               -->     /foo/
          +     * /foo/./              -->     /foo/
          +     * /foo/../bar          -->     /bar
          +     * /foo/../bar/         -->     /bar/
          +     * /foo/../bar/../baz   -->     /baz
          +     * //foo//./bar         -->     /foo/bar
          +     * /../                 -->     null
          +     * 
          + * + * @param path the path to normalize + * @return the normalized String, or null if too many ..'s. + * @deprecated use {@code org.apache.commons.io.FileNameUtils.normalize()} + */ + @Deprecated + @Nonnull public static String normalize( @Nonnull final String path ) + { + String normalized = path; + // Resolve occurrences of "//" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "//" ); + if ( index < 0 ) + { + break; + } + normalized = normalized.substring( 0, index ) + normalized.substring( index + 1 ); + } + + // Resolve occurrences of "/./" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "/./" ); + if ( index < 0 ) + { + break; + } + normalized = normalized.substring( 0, index ) + normalized.substring( index + 2 ); + } + + // Resolve occurrences of "/../" in the normalized path + while ( true ) + { + int index = normalized.indexOf( "/../" ); + if ( index < 0 ) + { + break; + } + if ( index == 0 ) + { + return null; // Trying to go outside our context + } + int index2 = normalized.lastIndexOf( '/', index - 1 ); + normalized = normalized.substring( 0, index2 ) + normalized.substring( index + 3 ); + } + + // Return the normalized path that we have completed + return normalized; + } + + /** + * Resolve a file filename to it's canonical form. If filename is + * relative (doesn't start with /), it will be resolved relative to + * baseFile, otherwise it is treated as a normal root-relative path. + * + * @param baseFile Where to resolve filename from, if filename is + * relative. + * @param filename absolute or relative file path to resolve + * @return the canonical File of filename + */ + @Nonnull public static File resolveFile( final File baseFile, @Nonnull String filename ) + { + String filenm = filename; + if ( '/' != File.separatorChar ) + { + filenm = filename.replace( '/', File.separatorChar ); + } + + if ( '\\' != File.separatorChar ) + { + filenm = filename.replace( '\\', File.separatorChar ); + } + + // deal with absolute files + if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) ) + { + File file = new File( filenm ); + + try + { + file = file.getCanonicalFile(); + } + catch ( final IOException ioe ) + { + // nop + } + + return file; + } + // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips + // them. However, I'm not sure about this UNC stuff. (JT) + final char[] chars = filename.toCharArray(); + final StringBuilder sb = new StringBuilder(); + + //remove duplicate file separators in succession - except + //on win32 at start of filename as UNC filenames can + //be \\AComputer\AShare\myfile.txt + int start = 0; + if ( '\\' == File.separatorChar ) + { + sb.append( filenm.charAt( 0 ) ); + start++; + } + + for ( int i = start; i < chars.length; i++ ) + { + final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1]; + + if ( !doubleSeparator ) + { + sb.append( chars[i] ); + } + } + + filenm = sb.toString(); + + //must be relative + File file = ( new File( baseFile, filenm ) ).getAbsoluteFile(); + + try + { + file = file.getCanonicalFile(); + } + catch ( final IOException ioe ) + { + // nop + } + + return file; + } + + /** + * Delete a file. If file is directory, delete it and all sub-directories. + * + * @param file the file path + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()} + */ + @Deprecated + public static void forceDelete( @Nonnull final String file ) + throws IOException + { + forceDelete( new File( file ) ); + } + + /** + * Delete a file. If file is directory, delete it and all sub-directories. + * + * @param file a file + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()} + */ + @Deprecated + public static void forceDelete( @Nonnull final File file ) + throws IOException + { + if ( file.isDirectory() ) + { + deleteDirectory( file ); + } + else + { + /* + * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a + * symlink whose target does not exist is deleted, too. + */ + boolean filePresent = file.getCanonicalFile().exists(); + if ( !deleteFile( file ) && filePresent ) + { + final String message = "File " + file + " unable to be deleted."; + throw new IOException( message ); + } + } + } + + /** + * Deletes a file. + * + * @param file the file to delete + * @throws IOException if the file cannot be deleted + * @deprecated use {@code java.nio.files.Files.delete(file.toPath())} + */ + @Deprecated + public static void delete( @Nonnull File file ) + throws IOException + { + Files.delete( file.toPath() ); + } + + /** + * @param file the file + * @return true / false + * @deprecated use {@code java.nio.files.Files.delete(file.toPath())} + */ + @Deprecated + public static boolean deleteLegacyStyle( @Nonnull File file ) + { + try + { + Files.delete( file.toPath() ); + return true; + } + catch ( IOException e ) + { + return false; + } + } + + /** + * Accommodate Windows bug encountered in both Sun and IBM JDKs. + * Others possible. If the delete does not work, call System.gc(), + * wait a little and try again. + * + * @param file a file + * @throws IOException if any + */ + private static boolean deleteFile( @Nonnull File file ) + throws IOException + { + if ( file.isDirectory() ) + { + throw new IOException( "File " + file + " isn't a file." ); + } + + if ( !deleteLegacyStyle( file ) ) + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + file = file.getCanonicalFile(); + } + + try + { + Thread.sleep( 10 ); + return deleteLegacyStyle( file ); + } + catch ( InterruptedException ex ) + { + return deleteLegacyStyle( file ); + } + } + + return true; + } + + + /** + * Make a directory. + * + * @param file not null + * @throws IOException if a file already exists with the specified name or the directory is unable to be created + * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS. + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + */ + public static void forceMkdir( @Nonnull final File file ) + throws IOException + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) + { + throw new IllegalArgumentException( + "The file (" + file.getAbsolutePath() + ") cannot contain any of the following characters: \n" + + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + } + + if ( file.exists() ) + { + if ( file.isFile() ) + { + final String message = + "File " + file + " exists and is " + "not a directory. Unable to create directory."; + throw new IOException( message ); + } + } + else + { + if ( !file.mkdirs() ) + { + final String message = "Unable to create directory " + file; + throw new IOException( message ); + } + } + } + + /** + * Recursively delete a directory. + * + * @param directory a directory + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()} + */ + @Deprecated + public static void deleteDirectory( @Nonnull final String directory ) + throws IOException + { + deleteDirectory( new File( directory ) ); + } + + /** + * Recursively delete a directory. + * + * @param directory a directory + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()} + */ + @Deprecated + public static void deleteDirectory( @Nonnull final File directory ) + throws IOException + { + if ( !directory.exists() ) + { + return; + } + + /* try delete the directory before its contents, which will take + * care of any directories that are really symbolic links. + */ + if ( deleteLegacyStyle( directory ) ) + { + return; + } + + cleanDirectory( directory ); + if ( !deleteLegacyStyle( directory ) ) + { + final String message = "Directory " + directory + " unable to be deleted."; + throw new IOException( message ); + } + } + + /** + * Remove all files from a directory without deleting it. + * + * @param directory a directory + * @throws IOException if any. This can leave cleaning in a half-finished state where + * some but not all files have been deleted. + * @deprecated use {@code org.apache.commons.io.FileUtils.cleanDirectory()} + */ + @Deprecated + public static void cleanDirectory( @Nonnull final File directory ) + throws IOException + { + if ( !directory.exists() ) + { + final String message = directory + " does not exist"; + throw new IllegalArgumentException( message ); + } + + if ( !directory.isDirectory() ) + { + final String message = directory + " is not a directory"; + throw new IllegalArgumentException( message ); + } + + IOException exception = null; + + final File[] files = directory.listFiles(); + + if ( files == null ) + { + return; + } + + for ( final File file : files ) + { + try + { + forceDelete( file ); + } + catch ( final IOException ioe ) + { + exception = ioe; + } + } + + if ( null != exception ) + { + throw exception; + } + } + + /** + * Recursively count size of a directory. + * + * @param directory a directory + * @return size of directory in bytes + * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()} + */ + @Deprecated + public static long sizeOfDirectory( @Nonnull final String directory ) + { + return sizeOfDirectory( new File( directory ) ); + } + + /** + * Recursively count size of a directory. + * + * @param directory a directory + * @return size of directory in bytes + * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()} + */ + @Deprecated + public static long sizeOfDirectory( @Nonnull final File directory ) + { + if ( !directory.exists() ) + { + final String message = directory + " does not exist"; + throw new IllegalArgumentException( message ); + } + + if ( !directory.isDirectory() ) + { + final String message = directory + " is not a directory"; + throw new IllegalArgumentException( message ); + } + + long size = 0; + + final File[] files = directory.listFiles(); + if ( files == null ) + { + throw new IllegalArgumentException( "Problems reading directory" ); + } + + for ( final File file : files ) + { + if ( file.isDirectory() ) + { + size += sizeOfDirectory( file ); + } + else + { + size += file.length(); + } + } + + return size; + } + + /** + * Return the files contained in the directory, using inclusion and exclusion Ant patterns, + * including the directory name in each of the files + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @return a list of File objects + * @throws IOException in case of failure. + * @see #getFileNames(File, String, String, boolean) + */ + @Nonnull + public static List getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes ) + throws IOException + { + return getFiles( directory, includes, excludes, true ); + } + + /** + * Return the files contained in the directory, using inclusion and exclusion Ant patterns + * + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each file + * @return a list of File objects + * @throws IOException in case of failure. + * @see #getFileNames(File, String, String, boolean) + */ + @Nonnull + public static List getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, + boolean includeBasedir ) + throws IOException + { + List fileNames = getFileNames( directory, includes, excludes, includeBasedir ); + + List files = new ArrayList(); + + for ( String filename : fileNames ) + { + files.add( new File( filename ) ); + } + + return files; + } + + /** + * Return a list of files as String depending options. + * This method use case sensitive file name. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @return a list of file names + * @throws IOException in case of failure + */ + @Nonnull public static List getFileNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir ) + throws IOException + { + return getFileNames( directory, includes, excludes, includeBasedir, true ); + } + + /** + * Return a list of files as String depending options. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each String of file + * @param isCaseSensitive true if case sensitive + * @return a list of files as String + * @throws IOException + */ + @Nonnull private static List getFileNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive ) + throws IOException + { + return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false ); + } + + /** + * Return a list of directories as String depending options. + * This method use case sensitive file name. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base dir in each String of file + * @return a list of directories as String + * @throws IOException in case of failure. + */ + @Nonnull public static List getDirectoryNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir ) + throws IOException + { + return getDirectoryNames( directory, includes, excludes, includeBasedir, true ); + } + + /** + * Return a list of directories as Strings. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @param isCaseSensitive true if case sensitive + * @return a list of directories as String + * @throws IOException in case of failure + */ + @Nonnull public static List getDirectoryNames( @Nonnull File directory, @Nullable String includes, + @Nullable String excludes, boolean includeBasedir, + boolean isCaseSensitive ) + throws IOException + { + return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true ); + } + + /** + * Return a list of file names as Strings. + * + * @param directory the directory to scan + * @param includes the Ant includes pattern, comma separated + * @param excludes the Ant excludes pattern, comma separated + * @param includeBasedir true to include the base directory in each String of file + * @param isCaseSensitive true if case sensitive + * @param getFiles true to include regular files + * @param getDirectories true to include directories + * @return a list of file names + */ +/** + * Return a list of file names as Strings. + * + * @param directory + * the directory to scan + * @param includes + * the Ant includes pattern, comma separated + * @param excludes + * the Ant excludes pattern, comma separated + * @param includeBasedir + * true to include the base directory in each String of file + * @param isCaseSensitive + * true if case sensitive + * @param getFiles + * true to include regular files + * @param getDirectories + * true to include directories + * @return a list of file names + */ +public static java.util.List getFileAndDirectoryNames(java.io.File directory, @javax.annotation.Nullable +java.lang.String includes, @javax.annotation.Nullable +java.lang.String excludes, boolean includeBasedir, boolean isCaseSensitive, boolean getFiles, boolean getDirectories) { + org.apache.maven.shared.utils.io.DirectoryScanner scanner = new org.apache.maven.shared.utils.io.DirectoryScanner(); + scanner.setBasedir(directory); + if (includes != null) { + scanner.setIncludes(org.apache.maven.shared.utils.StringUtils.split(includes, ",")); + } + { + scanner.setExcludes(org.apache.maven.shared.utils.StringUtils.split(/* NPEX_NULL_EXP */ + excludes, ",")); + } + scanner.setCaseSensitive(isCaseSensitive); + scanner.scan(); + java.util.List list = new java.util.ArrayList(); + if (getFiles) { + java.lang.String[] files = scanner.getIncludedFiles(); + for (java.lang.String file : files) { + if (includeBasedir) { + list.add((directory + org.apache.maven.shared.utils.io.FileUtils.FS) + file); + } else { + list.add(file); + } + } + } + if (getDirectories) { + java.lang.String[] directories = scanner.getIncludedDirectories(); + for (java.lang.String directory1 : directories) { + if (includeBasedir) { + list.add((directory + org.apache.maven.shared.utils.io.FileUtils.FS) + directory1); + } else { + list.add(directory1); + } + } + } + return list; +} + + /** + * Copy the contents of a directory into another one. + * + * @param sourceDirectory the source directory + * @param destinationDirectory the target directory + * @throws IOException if any + */ + public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) + throws IOException + { + copyDirectory( sourceDirectory, destinationDirectory, "**", null ); + } + + /** + * Copy the contents of a directory into another one. + * + * @param sourceDirectory the source directory + * @param destinationDirectory the target directory + * @param includes Ant include pattern + * @param excludes Ant exclude pattern + * @throws IOException if any + * @see #getFiles(File, String, String) + */ + public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, + @Nullable String includes, @Nullable String excludes ) + throws IOException + { + if ( !sourceDirectory.exists() ) + { + return; + } + + List files = getFiles( sourceDirectory, includes, excludes ); + + for ( File file : files ) + { + copyFileToDirectory( file, destinationDirectory ); + } + } + + /** + * Copies an entire directory structure. + *

          + * Note: + *

            + *
          • It will include empty directories. + *
          • The sourceDirectory must exist. + *
          + * + * @param sourceDirectory the source dir + * @param destinationDirectory the target dir + * @throws IOException if any + * @deprecated use {@code org.apache.commons.io.FileUtils.copyDirectory()} + */ + @Deprecated + public static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) + throws IOException + { + copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, false ); + } + + private static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, + File rootDestinationDirectory, boolean onlyModifiedFiles ) + throws IOException + { + //noinspection ConstantConditions + if ( sourceDirectory == null ) + { + throw new IOException( "source directory can't be null." ); + } + + //noinspection ConstantConditions + if ( destinationDirectory == null ) + { + throw new IOException( "destination directory can't be null." ); + } + + if ( sourceDirectory.equals( destinationDirectory ) ) + { + throw new IOException( "source and destination are the same directory." ); + } + + if ( !sourceDirectory.exists() ) + { + throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); + } + + File[] files = sourceDirectory.listFiles(); + + if ( files == null ) + { + return; + } + + String sourcePath = sourceDirectory.getAbsolutePath(); + + for ( File file : files ) + { + if ( file.equals( rootDestinationDirectory ) ) + { + // We don't copy the destination directory in itself + continue; + } + + String dest = file.getAbsolutePath(); + + dest = dest.substring( sourcePath.length() + 1 ); + + File destination = new File( destinationDirectory, dest ); + + if ( file.isFile() ) + { + destination = destination.getParentFile(); + + if ( onlyModifiedFiles ) + { + copyFileToDirectoryIfModified( file, destination ); + } + else + { + copyFileToDirectory( file, destination ); + } + } + else if ( file.isDirectory() ) + { + if ( !destination.exists() && !destination.mkdirs() ) + { + throw new IOException( + "Could not create destination directory '" + destination.getAbsolutePath() + "'." ); + } + + copyDirectoryStructure( file, destination, rootDestinationDirectory, onlyModifiedFiles ); + } + else + { + throw new IOException( "Unknown file type: " + file.getAbsolutePath() ); + } + } + } + + /** + * Renames a file, even if that involves crossing file system boundaries. + *

          + *

          This will remove to (if it exists), ensure that + * to's parent directory exists and move + * from, which involves deleting from as + * well.

          + * + * @param from the file to move + * @param to the new file name + * @throws IOException if anything bad happens during this process. + * Note that to may have been deleted already when this happens. + * @deprecated use {@code java.nio.Files.move()} + */ + @Deprecated + public static void rename( @Nonnull File from, @Nonnull File to ) + throws IOException + { + if ( to.exists() && !deleteLegacyStyle( to ) ) + { + throw new IOException( "Failed to delete " + to + " while trying to rename " + from ); + } + + File parent = to.getParentFile(); + if ( parent != null && !parent.exists() && !parent.mkdirs() ) + { + throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from ); + } + + if ( !from.renameTo( to ) ) + { + copyFile( from, to ); + if ( !deleteLegacyStyle( from ) ) + { + throw new IOException( "Failed to delete " + from + " while trying to rename it." ); + } + } + } + + /** + * Create a temporary file in a given directory. + *

          + *

          The file denoted by the returned abstract pathname did not + * exist before this method was invoked, any subsequent invocation + * of this method will yield a different file name.

          + *

          + * The filename is prefixNNNNNsuffix where NNNN is a random number + *

          + *

          This method is different to {@link File#createTempFile(String, String, File)} + * as it doesn't create the file itself. + * It uses the location pointed to by java.io.tmpdir + * when the parentDir attribute is null.

          + *

          To automatically delete the file created by this method, use the + * {@link File#deleteOnExit()} method.

          + * + * @param prefix prefix before the random number + * @param suffix file extension; include the '.' + * @param parentDir directory to create the temporary file in -java.io.tmpdir + * used if not specified + * @return a File reference to the new temporary file. + * @deprecated use {@code java.nio.Files.createTempFile()} + */ + @Deprecated + public static File createTempFile( @Nonnull String prefix, @Nonnull String suffix, @Nullable File parentDir ) + { + File result; + String parent = System.getProperty( "java.io.tmpdir" ); + if ( parentDir != null ) + { + parent = parentDir.getPath(); + } + DecimalFormat fmt = new DecimalFormat( "#####" ); + SecureRandom secureRandom = new SecureRandom(); + long secureInitializer = secureRandom.nextLong(); + Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() ); + do + { + result = new File( parent, prefix + fmt.format( positiveRandom( rand ) ) + suffix ); + } + while ( result.exists() ); + + return result; + } + + private static int positiveRandom( Random rand ) + { + int a = rand.nextInt(); + while ( a == Integer.MIN_VALUE ) + { + a = rand.nextInt(); + } + return Math.abs( a ); + } + + /** + * If wrappers is null or empty, the file will be copied only if to.lastModified() < from.lastModified() + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} + * @throws IOException if an IO error occurs during copying or filtering + */ + public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, + @Nullable FilterWrapper... wrappers ) + throws IOException + { + copyFile( from, to, encoding, wrappers, false ); + } + + /** + * Wrapper class for Filter. + */ + public abstract static class FilterWrapper + { + /** + * @param fileReader {@link Reader} + * @return the Reader instance + */ + public abstract Reader getReader( Reader fileReader ); + } + + /** + * If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if + * overwrite is true + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} + * @param overwrite if true and wrappers is null or empty, the file will be copied even if + * to.lastModified() < from.lastModified() + * @throws IOException if an IO error occurs during copying or filtering + */ + public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, + @Nullable FilterWrapper[] wrappers, boolean overwrite ) + throws IOException + { + if ( wrappers == null || wrappers.length == 0 ) + { + if ( overwrite || to.lastModified() < from.lastModified() ) + { + copyFile( from, to ); + } + } + else + { + Charset charset = charset( encoding ); + + // buffer so it isn't reading a byte at a time! + try ( Reader fileReader = Files.newBufferedReader( from.toPath(), charset ) ) + { + Reader wrapped = fileReader; + for ( FilterWrapper wrapper : wrappers ) + { + wrapped = wrapper.getReader( wrapped ); + } + + if ( overwrite || !to.exists() ) + { + try ( Writer fileWriter = Files.newBufferedWriter( to.toPath(), charset ) ) + { + IOUtil.copy( wrapped, fileWriter ); + } + } + else + { + CharsetEncoder encoder = charset.newEncoder(); + + int totalBufferSize = FILE_COPY_BUFFER_SIZE; + + int charBufferSize = ( int ) Math.floor( totalBufferSize / ( 2 + 2 * encoder.maxBytesPerChar() ) ); + int byteBufferSize = ( int ) Math.ceil( charBufferSize * encoder.maxBytesPerChar() ); + + CharBuffer newChars = CharBuffer.allocate( charBufferSize ); + ByteBuffer newBytes = ByteBuffer.allocate( byteBufferSize ); + ByteBuffer existingBytes = ByteBuffer.allocate( byteBufferSize ); + + CoderResult coderResult; + int existingRead; + boolean writing = false; + + try ( final RandomAccessFile existing = new RandomAccessFile( to, "rw" ) ) + { + int n; + while ( -1 != ( n = wrapped.read( newChars ) ) ) + { + ( ( Buffer ) newChars ).flip(); + + coderResult = encoder.encode( newChars, newBytes, n != 0 ); + if ( coderResult.isError() ) + { + coderResult.throwException(); + } + + ( ( Buffer ) newBytes ).flip(); + + if ( !writing ) + { + existingRead = existing.read( existingBytes.array(), 0, newBytes.remaining() ); + ( ( Buffer ) existingBytes ).position( existingRead ); + ( ( Buffer ) existingBytes ).flip(); + + if ( newBytes.compareTo( existingBytes ) != 0 ) + { + writing = true; + if ( existingRead > 0 ) + { + existing.seek( existing.getFilePointer() - existingRead ); + } + } + } + + if ( writing ) + { + existing.write( newBytes.array(), 0, newBytes.remaining() ); + } + + ( ( Buffer ) newChars ).clear(); + ( ( Buffer ) newBytes ).clear(); + ( ( Buffer ) existingBytes ).clear(); + } + + if ( existing.length() > existing.getFilePointer() ) + { + existing.setLength( existing.getFilePointer() ); + } + } + } + } + } + + copyFilePermissions( from, to ); + } + + /** + * Attempts to copy file permissions from the source to the destination file. + * Initially attempts to copy posix file permissions, assuming that the files are both on posix filesystems. + * If the initial attempts fail then a second attempt using less precise permissions model. + * Note that permissions are copied on a best-efforts basis, + * failure to copy permissions will not result in an exception. + * + * @param source the file to copy permissions from. + * @param destination the file to copy permissions to. + */ + private static void copyFilePermissions( @Nonnull File source, @Nonnull File destination ) + throws IOException + { + try + { + // attempt to copy posix file permissions + Files.setPosixFilePermissions( + destination.toPath(), + Files.getPosixFilePermissions( source.toPath() ) + ); + } + catch ( UnsupportedOperationException e ) + { + // fallback to setting partial permissions + destination.setExecutable( source.canExecute() ); + destination.setReadable( source.canRead() ); + destination.setWritable( source.canWrite() ); + } + } + + /** + * Note: the file content is read with platform encoding. + * + * @param file the file + * @return a List containing every every line not starting with # and not empty + * @throws IOException if any + * @deprecated assumes the platform default character set + */ + @Deprecated + @Nonnull public static List loadFile( @Nonnull File file ) + throws IOException + { + List lines = new ArrayList(); + + if ( file.exists() ) + { + try ( BufferedReader reader = Files.newBufferedReader( file.toPath(), Charset.defaultCharset() ) ) + { + for ( String line = reader.readLine(); line != null; line = reader.readLine() ) + { + line = line.trim(); + if ( !line.startsWith( "#" ) && line.length() != 0 ) + { + lines.add( line ); + } + } + } + } + + return lines; + + } + + /** + * Returns the named charset or the default charset. + * @param encoding the name or alias of the charset, null or empty + * @return A charset object for the named or default charset. + */ + private static Charset charset( String encoding ) + { + if ( encoding == null || encoding.isEmpty() ) + { + return Charset.defaultCharset(); + } + else + { + return Charset.forName( encoding ); + } + } + + /** + * For Windows OS, check if the file name contains any of the following characters: + * ":", "*", "?", "\"", "<", ">", "|" + * + * @param f not null file + * @return false if the file path contains any of forbidden Windows characters, + * true if the Os is not Windows or if the file path respect the Windows constraints. + * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME + */ + private static boolean isValidWindowsFileName( @Nonnull File f ) + { + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 ) + { + return false; + } + + if ( f.getParentFile() != null ) + { + return isValidWindowsFileName( f.getParentFile() ); + } + } + + return true; + } + + /** + * Checks whether a given file is a symbolic link. + * + * @param file the file to check + * @throws IOException in case of failure. + * @return true if symbolic link false otherwise. + * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())} + */ + @Deprecated + public static boolean isSymbolicLink( @Nonnull final File file ) + throws IOException + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * Checks whether a given file is a symbolic link. + * + * @param file the file to check + * @return true if and only if we reliably can say this is a symlink + * + * @throws IOException in case of failure + * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())} + */ + @Deprecated + public static boolean isSymbolicLinkForSure( @Nonnull final File file ) + throws IOException + { + return Files.isSymbolicLink( file.toPath() ); + } + + /** + * Create a new symbolic link, possibly replacing an existing symbolic link. + * + * @param symlink the link name + * @param target the target + * @return the linked file + * @throws IOException in case of an error + * @see {@code java.nio.file.Files.createSymbolicLink(Path)} which creates a new + * symbolic link but does not replace exsiting symbolic links + */ + @Nonnull public static File createSymbolicLink( @Nonnull File symlink, @Nonnull File target ) + throws IOException + { + final Path symlinkPath = symlink.toPath(); + + if ( Files.exists( symlinkPath ) ) + { + if ( target.equals( Files.readSymbolicLink( symlinkPath ).toFile() ) ) + { + return symlink; + } + + Files.delete( symlinkPath ); + } + + return Files.createSymbolicLink( symlinkPath, target.toPath() ).toFile(); + } +} diff --git a/Java/maven-shared-utils-FileUtils_1558/metadata.json b/Java/maven-shared-utils-FileUtils_1558/metadata.json new file mode 100644 index 000000000..2268d6100 --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1558/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-FileUtils_1558", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/FileUtils.java", + "line": 1573, + "npe_method": "getFileAndDirectoryNames", + "deref_field": "excludes", + "npe_class": "FileUtils", + "repo": "maven-shared-utils", + "bug_id": "FileUtils_1558" + } +} diff --git a/Java/maven-shared-utils-FileUtils_1558/npe.json b/Java/maven-shared-utils-FileUtils_1558/npe.json new file mode 100644 index 000000000..479319c5e --- /dev/null +++ b/Java/maven-shared-utils-FileUtils_1558/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/FileUtils.java", + "line": 1573, + "npe_method": "getFileAndDirectoryNames", + "deref_field": "excludes", + "npe_class": "FileUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-IOUtil_1040/Dockerfile b/Java/maven-shared-utils-IOUtil_1040/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1040/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-IOUtil_1040/buggy.java b/Java/maven-shared-utils-IOUtil_1040/buggy.java new file mode 100644 index 000000000..73a24e8df --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1040/buggy.java @@ -0,0 +1,1456 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.channels.Channel; + +/** + * General IO Stream manipulation. + *

          + * This class provides static utility methods for input/output operations, particularly buffered + * copying between sources (InputStream, Reader, String and + * byte[]) and destinations (OutputStream, Writer, + * String and byte[]). + * + *

          Unless otherwise noted, these copy methods do not flush or close the + * streams. Often, doing so would require making non-portable assumptions about the streams' origin + * and further use. This means that both streams' close() methods must be called after + * copying. if one omits this step, then the stream resources (sockets, file descriptors) are + * released when the associated Stream is garbage-collected. It is not a good idea to rely on this + * mechanism.

          + * + *

          For each copy method, a variant is provided that allows the caller to specify the + * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this + * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data + * transfers.

          + *

          For byte-to-char methods, a copy variant allows the encoding to be selected + * (otherwise the platform default is used).

          + *

          The copy methods use an internal buffer when copying. It is therefore advisable + * not to deliberately wrap the stream arguments to the copy methods in + * Buffered* streams. For example, don't do the + * following:

          + *

          + * copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) ); + *

          + *

          The rationale is as follows:

          + * + *

          Imagine that an InputStream's read() is a very expensive operation, which would usually suggest + * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent + * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to + * fill an internal buffer, from which further read requests can inexpensively get + * their data (until the buffer runs out).

          + *

          However, the copy methods do the same thing, keeping an internal buffer, + * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers + * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer + * management hurts performance slightly (about 3%, according to some simple experiments).

          + * + * @author Peter Donald + * @author Jeff Turner + * @version CVS $Revision$ $Date$ + * + */ +public final class IOUtil +/* + * Behold, intrepid explorers; a map of this class: + * + * Method Input Output Dependency + * ------ ----- ------ ------- + * 1 copy InputStream OutputStream (primitive) + * 2 copy Reader Writer (primitive) + * + * 3 copy InputStream Writer 2 + * 4 toString InputStream String 3 + * 5 toByteArray InputStream byte[] 1 + * + * 6 copy Reader OutputStream 2 + * 7 toString Reader String 2 + * 8 toByteArray Reader byte[] 6 + * + * 9 copy String OutputStream 2 + * 10 copy String Writer (trivial) + * 11 toByteArray String byte[] 9 + * + * 12 copy byte[] Writer 3 + * 13 toString byte[] String 12 + * 14 copy byte[] OutputStream (trivial) + * + * + * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy + * using native Java copy methods. As there are method variants to specify buffer size and encoding, + * each row may correspond to up to 4 methods. + * + */ +{ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Private constructor to prevent instantiation. + */ + private IOUtil() + { + } + + /////////////////////////////////////////////////////////////// + // Core copy methods + /////////////////////////////////////////////////////////////// + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * @param input the stream to read from + * @param output the stream to write to + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * In Java 9 and later this is replaced by {@code InputStream.transferTo()}. + * + * @param input the stream to read from + * @param output the stream to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output, + final int bufferSize ) + throws IOException + { + final byte[] buffer = new byte[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final char[] buffer = new char[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + output.flush(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // InputStream -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // InputStream -> Writer + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> String + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @return the converted string + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> byte[] + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // Reader -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // Reader -> OutputStream + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output stream to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( input, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> String + + /** + * Get the contents of a Reader as a String. + * @param input the InputStream to read from + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a String. + * + * @param input the reader to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> byte[] + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // String -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // String -> OutputStream + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final StringReader in = new StringReader( input ); + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( in, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // String -> Writer + + /** + * Copy chars from a String to a Writer. + * + * @param input the string to write + * @param output resulting output {@link Writer} + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final Writer output ) + throws IOException + { + output.write( input ); + } + + /////////////////////////////////////////////////////////////// + // String -> byte[] + + /** + * Get the contents of a String as a byte[]. + * + * @param input the String to read from + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a String as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // byte[] -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // byte[] -> Writer + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the data to write + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final String encoding ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @param output The output buffer {@link Writer} + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> String + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param bufferSize size of internal buffer + * @param input the input bytes + * @return the created string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @param input input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> OutputStream + + /** + * Copy bytes from a byte[] to an OutputStream. + * + * @param input Input byte array. + * @param output output stream {@link OutputStream} + * @throws IOException in case of failure + * @deprecated inline this method + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final OutputStream output ) + throws IOException + { + output.write( input ); + } + + /** + * Compare the contents of two streams to determine if they are equal or not. + * + * @param input1 the first stream + * @param input2 the second stream + * @return true if the content of the streams are equal or they both don't exist, false otherwise + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.contentEquals()} + */ + @Deprecated + public static boolean contentEquals( @Nonnull final InputStream input1, @Nonnull final InputStream input2 ) + throws IOException + { + final InputStream bufferedInput1 = new BufferedInputStream( input1 ); + final InputStream bufferedInput2 = new BufferedInputStream( input2 ); + + int ch = bufferedInput1.read(); + while ( -1 != ch ) + { + final int ch2 = bufferedInput2.read(); + if ( ch != ch2 ) + { + return false; + } + ch = bufferedInput1.read(); + } + + final int ch2 = bufferedInput2.read(); + return -1 == ch2; + } + + // ---------------------------------------------------------------------- + // closeXXX() + // ---------------------------------------------------------------------- + + /** + * Closes a {@code Channel} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param channel The channel to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Channel channel ) + { + try + { + if ( channel != null ) + { + channel.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code InputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param inputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated +/** + * Closes an {@code InputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          + * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          + * Closeable resource1 = null;
          + * Closeable resource2 = null;
          + * try
          + * {
          + * // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          + * // If successful, resource1 != null.
          + * resource1 = ...
          + *
          + * // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          + * // If successful, resource2 != null. Not reached if an exception has been thrown above.
          + * resource2 = ...
          + *
          + * // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          + * // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          + * // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          + * // resource2 = something else here.
          + * resource1 ...
          + * resource2 ...
          + *
          + * // Finally, close the resources and set the variables to null indicating successful completion.
          + * // This may throw an exception. Not reached if an exception has been thrown above.
          + * resource1.close();
          + * resource1 = null;
          + * // This may throw an exception. Not reached if an exception has been thrown above.
          + * resource2.close();
          + * resource2 = null;
          + *
          + * // All resources are closed at this point and all operations (up to here) completed successfully without
          + * // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          + * }
          + * finally
          + * {
          + * // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          + * // exception this may produce to not stop the exception from the try block to be propagated. If the try
          + * // block completed successfully, all variables will have been set to null there and this will not do
          + * // anything. This is just to cleanup properly in case of an exception.
          + *
          + * IOUtil.close( resource1 );
          + * IOUtil.close( resource2 );
          + *
          + * // Without that utility method you would need to write the following:
          + * //
          + * // try
          + * // {
          + * //     if ( resource1 != null )
          + * //     {
          + * //         resource1.close();
          + * //     }
          + * // }
          + * // catch( IOException e )
          + * // {
          + * //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          + * //     propagate instead of this one.
          + * // }
          + * // finally
          + * // {
          + * //     try
          + * //     {
          + * //         if ( resource2 != null )
          + * //         {
          + * //             resource2.close();
          + * //         }
          + * //     }
          + * //     catch ( IOException e )
          + * //     {
          + * //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          + * //         propagate instead of this one.
          + * //     }
          + * // }
          + * }
          + * 
          + *

          + * + * @param inputStream + * The stream to close or {@code null}. + * @deprecated use try-with-resources + */ +public static void close(@javax.annotation.Nullable +java.io.InputStream inputStream) { + try { + { + /* NPEX_NULL_EXP */ + inputStream.close(); + } + } catch (java.io.IOException ex) { + // Suppressed + } +} + + /** + * Closes an {@code OutputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param outputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable OutputStream outputStream ) + { + try + { + if ( outputStream != null ) + { + outputStream.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes a {@code Reader} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param reader The reader to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Reader reader ) + { + try + { + if ( reader != null ) + { + reader.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes a {@code Writer} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param writer The writer to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Writer writer ) + { + try + { + if ( writer != null ) + { + writer.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } +} diff --git a/Java/maven-shared-utils-IOUtil_1040/metadata.json b/Java/maven-shared-utils-IOUtil_1040/metadata.json new file mode 100644 index 000000000..2e8abca40 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1040/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-IOUtil_1040", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/IOUtil.java", + "line": 1132, + "npe_method": "close", + "deref_field": "inputStream", + "npe_class": "IOUtil", + "repo": "maven-shared-utils", + "bug_id": "IOUtil_1040" + } +} diff --git a/Java/maven-shared-utils-IOUtil_1040/npe.json b/Java/maven-shared-utils-IOUtil_1040/npe.json new file mode 100644 index 000000000..4bc4116b0 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1040/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/IOUtil.java", + "line": 1132, + "npe_method": "close", + "deref_field": "inputStream", + "npe_class": "IOUtil" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-IOUtil_1146/Dockerfile b/Java/maven-shared-utils-IOUtil_1146/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1146/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-IOUtil_1146/buggy.java b/Java/maven-shared-utils-IOUtil_1146/buggy.java new file mode 100644 index 000000000..209d8e9cc --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1146/buggy.java @@ -0,0 +1,1456 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.channels.Channel; + +/** + * General IO Stream manipulation. + *

          + * This class provides static utility methods for input/output operations, particularly buffered + * copying between sources (InputStream, Reader, String and + * byte[]) and destinations (OutputStream, Writer, + * String and byte[]). + * + *

          Unless otherwise noted, these copy methods do not flush or close the + * streams. Often, doing so would require making non-portable assumptions about the streams' origin + * and further use. This means that both streams' close() methods must be called after + * copying. if one omits this step, then the stream resources (sockets, file descriptors) are + * released when the associated Stream is garbage-collected. It is not a good idea to rely on this + * mechanism.

          + * + *

          For each copy method, a variant is provided that allows the caller to specify the + * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this + * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data + * transfers.

          + *

          For byte-to-char methods, a copy variant allows the encoding to be selected + * (otherwise the platform default is used).

          + *

          The copy methods use an internal buffer when copying. It is therefore advisable + * not to deliberately wrap the stream arguments to the copy methods in + * Buffered* streams. For example, don't do the + * following:

          + *

          + * copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) ); + *

          + *

          The rationale is as follows:

          + * + *

          Imagine that an InputStream's read() is a very expensive operation, which would usually suggest + * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent + * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to + * fill an internal buffer, from which further read requests can inexpensively get + * their data (until the buffer runs out).

          + *

          However, the copy methods do the same thing, keeping an internal buffer, + * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers + * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer + * management hurts performance slightly (about 3%, according to some simple experiments).

          + * + * @author Peter Donald + * @author Jeff Turner + * @version CVS $Revision$ $Date$ + * + */ +public final class IOUtil +/* + * Behold, intrepid explorers; a map of this class: + * + * Method Input Output Dependency + * ------ ----- ------ ------- + * 1 copy InputStream OutputStream (primitive) + * 2 copy Reader Writer (primitive) + * + * 3 copy InputStream Writer 2 + * 4 toString InputStream String 3 + * 5 toByteArray InputStream byte[] 1 + * + * 6 copy Reader OutputStream 2 + * 7 toString Reader String 2 + * 8 toByteArray Reader byte[] 6 + * + * 9 copy String OutputStream 2 + * 10 copy String Writer (trivial) + * 11 toByteArray String byte[] 9 + * + * 12 copy byte[] Writer 3 + * 13 toString byte[] String 12 + * 14 copy byte[] OutputStream (trivial) + * + * + * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy + * using native Java copy methods. As there are method variants to specify buffer size and encoding, + * each row may correspond to up to 4 methods. + * + */ +{ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Private constructor to prevent instantiation. + */ + private IOUtil() + { + } + + /////////////////////////////////////////////////////////////// + // Core copy methods + /////////////////////////////////////////////////////////////// + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * @param input the stream to read from + * @param output the stream to write to + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * In Java 9 and later this is replaced by {@code InputStream.transferTo()}. + * + * @param input the stream to read from + * @param output the stream to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output, + final int bufferSize ) + throws IOException + { + final byte[] buffer = new byte[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final char[] buffer = new char[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + output.flush(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // InputStream -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // InputStream -> Writer + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> String + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @return the converted string + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> byte[] + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // Reader -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // Reader -> OutputStream + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output stream to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( input, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> String + + /** + * Get the contents of a Reader as a String. + * @param input the InputStream to read from + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a String. + * + * @param input the reader to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> byte[] + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // String -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // String -> OutputStream + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final StringReader in = new StringReader( input ); + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( in, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // String -> Writer + + /** + * Copy chars from a String to a Writer. + * + * @param input the string to write + * @param output resulting output {@link Writer} + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final Writer output ) + throws IOException + { + output.write( input ); + } + + /////////////////////////////////////////////////////////////// + // String -> byte[] + + /** + * Get the contents of a String as a byte[]. + * + * @param input the String to read from + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a String as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // byte[] -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // byte[] -> Writer + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the data to write + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final String encoding ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @param output The output buffer {@link Writer} + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> String + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param bufferSize size of internal buffer + * @param input the input bytes + * @return the created string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @param input input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> OutputStream + + /** + * Copy bytes from a byte[] to an OutputStream. + * + * @param input Input byte array. + * @param output output stream {@link OutputStream} + * @throws IOException in case of failure + * @deprecated inline this method + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final OutputStream output ) + throws IOException + { + output.write( input ); + } + + /** + * Compare the contents of two streams to determine if they are equal or not. + * + * @param input1 the first stream + * @param input2 the second stream + * @return true if the content of the streams are equal or they both don't exist, false otherwise + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.contentEquals()} + */ + @Deprecated + public static boolean contentEquals( @Nonnull final InputStream input1, @Nonnull final InputStream input2 ) + throws IOException + { + final InputStream bufferedInput1 = new BufferedInputStream( input1 ); + final InputStream bufferedInput2 = new BufferedInputStream( input2 ); + + int ch = bufferedInput1.read(); + while ( -1 != ch ) + { + final int ch2 = bufferedInput2.read(); + if ( ch != ch2 ) + { + return false; + } + ch = bufferedInput1.read(); + } + + final int ch2 = bufferedInput2.read(); + return -1 == ch2; + } + + // ---------------------------------------------------------------------- + // closeXXX() + // ---------------------------------------------------------------------- + + /** + * Closes a {@code Channel} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param channel The channel to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Channel channel ) + { + try + { + if ( channel != null ) + { + channel.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code InputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param inputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable InputStream inputStream ) + { + try + { + if ( inputStream != null ) + { + inputStream.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code OutputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param outputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated +/** + * Closes an {@code OutputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          + * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          + * Closeable resource1 = null;
          + * Closeable resource2 = null;
          + * try
          + * {
          + * // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          + * // If successful, resource1 != null.
          + * resource1 = ...
          + *
          + * // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          + * // If successful, resource2 != null. Not reached if an exception has been thrown above.
          + * resource2 = ...
          + *
          + * // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          + * // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          + * // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          + * // resource2 = something else here.
          + * resource1 ...
          + * resource2 ...
          + *
          + * // Finally, close the resources and set the variables to null indicating successful completion.
          + * // This may throw an exception. Not reached if an exception has been thrown above.
          + * resource1.close();
          + * resource1 = null;
          + * // This may throw an exception. Not reached if an exception has been thrown above.
          + * resource2.close();
          + * resource2 = null;
          + *
          + * // All resources are closed at this point and all operations (up to here) completed successfully without
          + * // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          + * }
          + * finally
          + * {
          + * // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          + * // exception this may produce to not stop the exception from the try block to be propagated. If the try
          + * // block completed successfully, all variables will have been set to null there and this will not do
          + * // anything. This is just to cleanup properly in case of an exception.
          + *
          + * IOUtil.close( resource1 );
          + * IOUtil.close( resource2 );
          + *
          + * // Without that utility method you would need to write the following:
          + * //
          + * // try
          + * // {
          + * //     if ( resource1 != null )
          + * //     {
          + * //         resource1.close();
          + * //     }
          + * // }
          + * // catch( IOException e )
          + * // {
          + * //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          + * //     propagate instead of this one.
          + * // }
          + * // finally
          + * // {
          + * //     try
          + * //     {
          + * //         if ( resource2 != null )
          + * //         {
          + * //             resource2.close();
          + * //         }
          + * //     }
          + * //     catch ( IOException e )
          + * //     {
          + * //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          + * //         propagate instead of this one.
          + * //     }
          + * // }
          + * }
          + * 
          + *

          + * + * @param outputStream + * The stream to close or {@code null}. + * @deprecated use try-with-resources + */ +public static void close(@javax.annotation.Nullable +java.io.OutputStream outputStream) { + try { + { + /* NPEX_NULL_EXP */ + outputStream.close(); + } + } catch (java.io.IOException ex) { + // Suppressed + } +} + + /** + * Closes a {@code Reader} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param reader The reader to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Reader reader ) + { + try + { + if ( reader != null ) + { + reader.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes a {@code Writer} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param writer The writer to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Writer writer ) + { + try + { + if ( writer != null ) + { + writer.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } +} diff --git a/Java/maven-shared-utils-IOUtil_1146/metadata.json b/Java/maven-shared-utils-IOUtil_1146/metadata.json new file mode 100644 index 000000000..a475c81f9 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1146/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-IOUtil_1146", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/IOUtil.java", + "line": 1238, + "npe_method": "close", + "deref_field": "outputStream", + "npe_class": "IOUtil", + "repo": "maven-shared-utils", + "bug_id": "IOUtil_1146" + } +} diff --git a/Java/maven-shared-utils-IOUtil_1146/npe.json b/Java/maven-shared-utils-IOUtil_1146/npe.json new file mode 100644 index 000000000..337bbf624 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1146/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/IOUtil.java", + "line": 1238, + "npe_method": "close", + "deref_field": "outputStream", + "npe_class": "IOUtil" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-IOUtil_1252/Dockerfile b/Java/maven-shared-utils-IOUtil_1252/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1252/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-IOUtil_1252/buggy.java b/Java/maven-shared-utils-IOUtil_1252/buggy.java new file mode 100644 index 000000000..f81b812dd --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1252/buggy.java @@ -0,0 +1,1456 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.channels.Channel; + +/** + * General IO Stream manipulation. + *

          + * This class provides static utility methods for input/output operations, particularly buffered + * copying between sources (InputStream, Reader, String and + * byte[]) and destinations (OutputStream, Writer, + * String and byte[]). + * + *

          Unless otherwise noted, these copy methods do not flush or close the + * streams. Often, doing so would require making non-portable assumptions about the streams' origin + * and further use. This means that both streams' close() methods must be called after + * copying. if one omits this step, then the stream resources (sockets, file descriptors) are + * released when the associated Stream is garbage-collected. It is not a good idea to rely on this + * mechanism.

          + * + *

          For each copy method, a variant is provided that allows the caller to specify the + * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this + * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data + * transfers.

          + *

          For byte-to-char methods, a copy variant allows the encoding to be selected + * (otherwise the platform default is used).

          + *

          The copy methods use an internal buffer when copying. It is therefore advisable + * not to deliberately wrap the stream arguments to the copy methods in + * Buffered* streams. For example, don't do the + * following:

          + *

          + * copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) ); + *

          + *

          The rationale is as follows:

          + * + *

          Imagine that an InputStream's read() is a very expensive operation, which would usually suggest + * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent + * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to + * fill an internal buffer, from which further read requests can inexpensively get + * their data (until the buffer runs out).

          + *

          However, the copy methods do the same thing, keeping an internal buffer, + * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers + * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer + * management hurts performance slightly (about 3%, according to some simple experiments).

          + * + * @author Peter Donald + * @author Jeff Turner + * @version CVS $Revision$ $Date$ + * + */ +public final class IOUtil +/* + * Behold, intrepid explorers; a map of this class: + * + * Method Input Output Dependency + * ------ ----- ------ ------- + * 1 copy InputStream OutputStream (primitive) + * 2 copy Reader Writer (primitive) + * + * 3 copy InputStream Writer 2 + * 4 toString InputStream String 3 + * 5 toByteArray InputStream byte[] 1 + * + * 6 copy Reader OutputStream 2 + * 7 toString Reader String 2 + * 8 toByteArray Reader byte[] 6 + * + * 9 copy String OutputStream 2 + * 10 copy String Writer (trivial) + * 11 toByteArray String byte[] 9 + * + * 12 copy byte[] Writer 3 + * 13 toString byte[] String 12 + * 14 copy byte[] OutputStream (trivial) + * + * + * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy + * using native Java copy methods. As there are method variants to specify buffer size and encoding, + * each row may correspond to up to 4 methods. + * + */ +{ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Private constructor to prevent instantiation. + */ + private IOUtil() + { + } + + /////////////////////////////////////////////////////////////// + // Core copy methods + /////////////////////////////////////////////////////////////// + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * @param input the stream to read from + * @param output the stream to write to + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * In Java 9 and later this is replaced by {@code InputStream.transferTo()}. + * + * @param input the stream to read from + * @param output the stream to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output, + final int bufferSize ) + throws IOException + { + final byte[] buffer = new byte[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final char[] buffer = new char[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + output.flush(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // InputStream -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // InputStream -> Writer + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> String + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @return the converted string + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> byte[] + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // Reader -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // Reader -> OutputStream + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output stream to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( input, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> String + + /** + * Get the contents of a Reader as a String. + * @param input the InputStream to read from + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a String. + * + * @param input the reader to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> byte[] + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // String -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // String -> OutputStream + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final StringReader in = new StringReader( input ); + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( in, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // String -> Writer + + /** + * Copy chars from a String to a Writer. + * + * @param input the string to write + * @param output resulting output {@link Writer} + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final Writer output ) + throws IOException + { + output.write( input ); + } + + /////////////////////////////////////////////////////////////// + // String -> byte[] + + /** + * Get the contents of a String as a byte[]. + * + * @param input the String to read from + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a String as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // byte[] -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // byte[] -> Writer + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the data to write + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final String encoding ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @param output The output buffer {@link Writer} + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> String + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param bufferSize size of internal buffer + * @param input the input bytes + * @return the created string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @param input input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> OutputStream + + /** + * Copy bytes from a byte[] to an OutputStream. + * + * @param input Input byte array. + * @param output output stream {@link OutputStream} + * @throws IOException in case of failure + * @deprecated inline this method + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final OutputStream output ) + throws IOException + { + output.write( input ); + } + + /** + * Compare the contents of two streams to determine if they are equal or not. + * + * @param input1 the first stream + * @param input2 the second stream + * @return true if the content of the streams are equal or they both don't exist, false otherwise + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.contentEquals()} + */ + @Deprecated + public static boolean contentEquals( @Nonnull final InputStream input1, @Nonnull final InputStream input2 ) + throws IOException + { + final InputStream bufferedInput1 = new BufferedInputStream( input1 ); + final InputStream bufferedInput2 = new BufferedInputStream( input2 ); + + int ch = bufferedInput1.read(); + while ( -1 != ch ) + { + final int ch2 = bufferedInput2.read(); + if ( ch != ch2 ) + { + return false; + } + ch = bufferedInput1.read(); + } + + final int ch2 = bufferedInput2.read(); + return -1 == ch2; + } + + // ---------------------------------------------------------------------- + // closeXXX() + // ---------------------------------------------------------------------- + + /** + * Closes a {@code Channel} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param channel The channel to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Channel channel ) + { + try + { + if ( channel != null ) + { + channel.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code InputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param inputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable InputStream inputStream ) + { + try + { + if ( inputStream != null ) + { + inputStream.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code OutputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param outputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable OutputStream outputStream ) + { + try + { + if ( outputStream != null ) + { + outputStream.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes a {@code Reader} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param reader The reader to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated +/** + * Closes a {@code Reader} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          + * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          + * Closeable resource1 = null;
          + * Closeable resource2 = null;
          + * try
          + * {
          + * // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          + * // If successful, resource1 != null.
          + * resource1 = ...
          + *
          + * // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          + * // If successful, resource2 != null. Not reached if an exception has been thrown above.
          + * resource2 = ...
          + *
          + * // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          + * // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          + * // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          + * // resource2 = something else here.
          + * resource1 ...
          + * resource2 ...
          + *
          + * // Finally, close the resources and set the variables to null indicating successful completion.
          + * // This may throw an exception. Not reached if an exception has been thrown above.
          + * resource1.close();
          + * resource1 = null;
          + * // This may throw an exception. Not reached if an exception has been thrown above.
          + * resource2.close();
          + * resource2 = null;
          + *
          + * // All resources are closed at this point and all operations (up to here) completed successfully without
          + * // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          + * }
          + * finally
          + * {
          + * // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          + * // exception this may produce to not stop the exception from the try block to be propagated. If the try
          + * // block completed successfully, all variables will have been set to null there and this will not do
          + * // anything. This is just to cleanup properly in case of an exception.
          + *
          + * IOUtil.close( resource1 );
          + * IOUtil.close( resource2 );
          + *
          + * // Without that utility method you would need to write the following:
          + * //
          + * // try
          + * // {
          + * //     if ( resource1 != null )
          + * //     {
          + * //         resource1.close();
          + * //     }
          + * // }
          + * // catch( IOException e )
          + * // {
          + * //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          + * //     propagate instead of this one.
          + * // }
          + * // finally
          + * // {
          + * //     try
          + * //     {
          + * //         if ( resource2 != null )
          + * //         {
          + * //             resource2.close();
          + * //         }
          + * //     }
          + * //     catch ( IOException e )
          + * //     {
          + * //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          + * //         propagate instead of this one.
          + * //     }
          + * // }
          + * }
          + * 
          + *

          + * + * @param reader + * The reader to close or {@code null}. + * @deprecated use try-with-resources + */ +public static void close(@javax.annotation.Nullable +java.io.Reader reader) { + try { + { + /* NPEX_NULL_EXP */ + reader.close(); + } + } catch (java.io.IOException ex) { + // Suppressed + } +} + + /** + * Closes a {@code Writer} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param writer The writer to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Writer writer ) + { + try + { + if ( writer != null ) + { + writer.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } +} diff --git a/Java/maven-shared-utils-IOUtil_1252/metadata.json b/Java/maven-shared-utils-IOUtil_1252/metadata.json new file mode 100644 index 000000000..aa5d4e4ad --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1252/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-IOUtil_1252", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/IOUtil.java", + "line": 1344, + "npe_method": "close", + "deref_field": "reader", + "npe_class": "IOUtil", + "repo": "maven-shared-utils", + "bug_id": "IOUtil_1252" + } +} diff --git a/Java/maven-shared-utils-IOUtil_1252/npe.json b/Java/maven-shared-utils-IOUtil_1252/npe.json new file mode 100644 index 000000000..ab4a886fb --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1252/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/IOUtil.java", + "line": 1344, + "npe_method": "close", + "deref_field": "reader", + "npe_class": "IOUtil" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-IOUtil_1358/Dockerfile b/Java/maven-shared-utils-IOUtil_1358/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1358/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-IOUtil_1358/buggy.java b/Java/maven-shared-utils-IOUtil_1358/buggy.java new file mode 100644 index 000000000..0810fa65e --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1358/buggy.java @@ -0,0 +1,1456 @@ +package org.apache.maven.shared.utils.io; + +/* + * 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. + */ + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.channels.Channel; + +/** + * General IO Stream manipulation. + *

          + * This class provides static utility methods for input/output operations, particularly buffered + * copying between sources (InputStream, Reader, String and + * byte[]) and destinations (OutputStream, Writer, + * String and byte[]). + * + *

          Unless otherwise noted, these copy methods do not flush or close the + * streams. Often, doing so would require making non-portable assumptions about the streams' origin + * and further use. This means that both streams' close() methods must be called after + * copying. if one omits this step, then the stream resources (sockets, file descriptors) are + * released when the associated Stream is garbage-collected. It is not a good idea to rely on this + * mechanism.

          + * + *

          For each copy method, a variant is provided that allows the caller to specify the + * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this + * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data + * transfers.

          + *

          For byte-to-char methods, a copy variant allows the encoding to be selected + * (otherwise the platform default is used).

          + *

          The copy methods use an internal buffer when copying. It is therefore advisable + * not to deliberately wrap the stream arguments to the copy methods in + * Buffered* streams. For example, don't do the + * following:

          + *

          + * copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) ); + *

          + *

          The rationale is as follows:

          + * + *

          Imagine that an InputStream's read() is a very expensive operation, which would usually suggest + * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent + * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to + * fill an internal buffer, from which further read requests can inexpensively get + * their data (until the buffer runs out).

          + *

          However, the copy methods do the same thing, keeping an internal buffer, + * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers + * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer + * management hurts performance slightly (about 3%, according to some simple experiments).

          + * + * @author Peter Donald + * @author Jeff Turner + * @version CVS $Revision$ $Date$ + * + */ +public final class IOUtil +/* + * Behold, intrepid explorers; a map of this class: + * + * Method Input Output Dependency + * ------ ----- ------ ------- + * 1 copy InputStream OutputStream (primitive) + * 2 copy Reader Writer (primitive) + * + * 3 copy InputStream Writer 2 + * 4 toString InputStream String 3 + * 5 toByteArray InputStream byte[] 1 + * + * 6 copy Reader OutputStream 2 + * 7 toString Reader String 2 + * 8 toByteArray Reader byte[] 6 + * + * 9 copy String OutputStream 2 + * 10 copy String Writer (trivial) + * 11 toByteArray String byte[] 9 + * + * 12 copy byte[] Writer 3 + * 13 toString byte[] String 12 + * 14 copy byte[] OutputStream (trivial) + * + * + * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy + * using native Java copy methods. As there are method variants to specify buffer size and encoding, + * each row may correspond to up to 4 methods. + * + */ +{ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Private constructor to prevent instantiation. + */ + private IOUtil() + { + } + + /////////////////////////////////////////////////////////////// + // Core copy methods + /////////////////////////////////////////////////////////////// + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * @param input the stream to read from + * @param output the stream to write to + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy bytes from an InputStream to an OutputStream. + * + * In Java 9 and later this is replaced by {@code InputStream.transferTo()}. + * + * @param input the stream to read from + * @param output the stream to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of an error + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()} or in + * Java 9 and later {@code InputStream.transferTo()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output, + final int bufferSize ) + throws IOException + { + final byte[] buffer = new byte[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy chars from a Reader to a Writer. + * + * @param input the reader to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final char[] buffer = new char[bufferSize]; + int n; + while ( -1 != ( n = input.read( buffer ) ) ) + { + output.write( buffer, 0, n ); + } + output.flush(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // InputStream -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // InputStream -> Writer + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the reader to read from + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param input the input stream to read from + * @param output the writer to write to + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output ); + } + + /** + * Copy and convert bytes from an InputStream to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input stream to read from + * @param output the writer to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.copy()}. + */ + @Deprecated + public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, + @Nonnull final String encoding, final int bufferSize ) + throws IOException + { + final InputStreamReader in = new InputStreamReader( input, encoding ); + copy( in, output, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> String + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @return the converted string + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a String. + * + * @param input the InputStream to read from + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // InputStream -> byte[] + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of an InputStream as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.readFully()}. + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input, final int bufferSize ) + throws IOException + { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // Reader -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // Reader -> OutputStream + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output stream to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a Reader to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( input, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> String + + /** + * Get the contents of a Reader as a String. + * @param input the InputStream to read from + * @return The converted string. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a String. + * + * @param input the reader to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.toString()}. + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // Reader -> byte[] + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a Reader as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array. + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final Reader input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // String -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // String -> OutputStream + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Serialize chars from a String to bytes on an OutputStream, and + * flush the OutputStream. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final OutputStream output, final int bufferSize ) + throws IOException + { + final StringReader in = new StringReader( input ); + final OutputStreamWriter out = new OutputStreamWriter( output ); + copy( in, out, bufferSize ); + // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush + // here. + out.flush(); + } + + /////////////////////////////////////////////////////////////// + // String -> Writer + + /** + * Copy chars from a String to a Writer. + * + * @param input the string to write + * @param output resulting output {@link Writer} + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final String input, @Nonnull final Writer output ) + throws IOException + { + output.write( input ); + } + + /////////////////////////////////////////////////////////////// + // String -> byte[] + + /** + * Get the contents of a String as a byte[]. + * + * @param input the String to read from + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input ) + throws IOException + { + return toByteArray( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a String as a byte[]. + * + * @param input the InputStream to read from + * @param bufferSize size of internal buffer + * @return the resulting byte array + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static byte[] toByteArray( @Nonnull final String input, final int bufferSize ) + throws IOException + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy( input, output, bufferSize ); + return output.toByteArray(); + } + + /////////////////////////////////////////////////////////////// + // Derived copy methods + // byte[] -> * + /////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////// + // byte[] -> Writer + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output ) + throws IOException + { + copy( input, output, DEFAULT_BUFFER_SIZE ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param input the InputStream to read from + * @param output the output to write to + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, bufferSize ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the data to write + * @param output the writer to write to + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final String encoding ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding ); + } + + /** + * Copy and convert bytes from a byte[] to chars on a + * Writer, using the specified encoding. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @param output The output buffer {@link Writer} + * @param bufferSize size of internal buffer + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.write()}. + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final ByteArrayInputStream in = new ByteArrayInputStream( input ); + copy( in, output, encoding, bufferSize ); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> String + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input ) + throws IOException + { + return toString( input, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * The platform's default encoding is used for the byte-to-char conversion. + * + * @param bufferSize size of internal buffer + * @param input the input bytes + * @return the created string + * @throws IOException in case of failure + * @deprecated always specify a character encoding + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, bufferSize ); + return sw.toString(); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param input the input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding ) + throws IOException + { + return toString( input, encoding, DEFAULT_BUFFER_SIZE ); + } + + /** + * Get the contents of a byte[] as a String. + * + * @param encoding the name of a supported character encoding. See the + * IANA + * Charset Registry for a list of valid encoding types. + * @param bufferSize size of internal buffer + * @param input input bytes + * @return the resulting string + * @throws IOException in case of failure + * @deprecated use {@code new String(input, encoding)} + */ + @Deprecated + @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding, + final int bufferSize ) + throws IOException + { + final StringWriter sw = new StringWriter(); + copy( input, sw, encoding, bufferSize ); + return sw.toString(); + } + + /////////////////////////////////////////////////////////////// + // byte[] -> OutputStream + + /** + * Copy bytes from a byte[] to an OutputStream. + * + * @param input Input byte array. + * @param output output stream {@link OutputStream} + * @throws IOException in case of failure + * @deprecated inline this method + */ + @Deprecated + public static void copy( @Nonnull final byte[] input, @Nonnull final OutputStream output ) + throws IOException + { + output.write( input ); + } + + /** + * Compare the contents of two streams to determine if they are equal or not. + * + * @param input1 the first stream + * @param input2 the second stream + * @return true if the content of the streams are equal or they both don't exist, false otherwise + * @throws IOException in case of failure + * @deprecated use {@code org.apache.commons.io.IOUtils.contentEquals()} + */ + @Deprecated + public static boolean contentEquals( @Nonnull final InputStream input1, @Nonnull final InputStream input2 ) + throws IOException + { + final InputStream bufferedInput1 = new BufferedInputStream( input1 ); + final InputStream bufferedInput2 = new BufferedInputStream( input2 ); + + int ch = bufferedInput1.read(); + while ( -1 != ch ) + { + final int ch2 = bufferedInput2.read(); + if ( ch != ch2 ) + { + return false; + } + ch = bufferedInput1.read(); + } + + final int ch2 = bufferedInput2.read(); + return -1 == ch2; + } + + // ---------------------------------------------------------------------- + // closeXXX() + // ---------------------------------------------------------------------- + + /** + * Closes a {@code Channel} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param channel The channel to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Channel channel ) + { + try + { + if ( channel != null ) + { + channel.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code InputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param inputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable InputStream inputStream ) + { + try + { + if ( inputStream != null ) + { + inputStream.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes an {@code OutputStream} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param outputStream The stream to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable OutputStream outputStream ) + { + try + { + if ( outputStream != null ) + { + outputStream.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes a {@code Reader} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param reader The reader to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated + public static void close( @Nullable Reader reader ) + { + try + { + if ( reader != null ) + { + reader.close(); + } + } + catch ( IOException ex ) + { + // Suppressed + } + } + + /** + * Closes a {@code Writer} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          +     * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          +     * Closeable resource1 = null;
          +     * Closeable resource2 = null;
          +     * try
          +     * {
          +     *     // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          +     *     // If successful, resource1 != null.
          +     *     resource1 = ...
          +     *
          +     *     // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          +     *     // If successful, resource2 != null. Not reached if an exception has been thrown above.
          +     *     resource2 = ...
          +     *
          +     *     // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          +     *     // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          +     *     // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          +     *     // resource2 = something else here.
          +     *     resource1 ...
          +     *     resource2 ...
          +     *
          +     *     // Finally, close the resources and set the variables to null indicating successful completion.
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource1.close();
          +     *     resource1 = null;
          +     *     // This may throw an exception. Not reached if an exception has been thrown above.
          +     *     resource2.close();
          +     *     resource2 = null;
          +     *
          +     *     // All resources are closed at this point and all operations (up to here) completed successfully without
          +     *     // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          +     * }
          +     * finally
          +     * {
          +     *     // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          +     *     // exception this may produce to not stop the exception from the try block to be propagated. If the try
          +     *     // block completed successfully, all variables will have been set to null there and this will not do
          +     *     // anything. This is just to cleanup properly in case of an exception.
          +     *
          +     *     IOUtil.close( resource1 );
          +     *     IOUtil.close( resource2 );
          +     *
          +     *     // Without that utility method you would need to write the following:
          +     *     //
          +     *     // try
          +     *     // {
          +     *     //     if ( resource1 != null )
          +     *     //     {
          +     *     //         resource1.close();
          +     *     //     }
          +     *     // }
          +     *     // catch( IOException e )
          +     *     // {
          +     *     //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          +     *     //     propagate instead of this one.
          +     *     // }
          +     *     // finally
          +     *     // {
          +     *     //     try
          +     *     //     {
          +     *     //         if ( resource2 != null )
          +     *     //         {
          +     *     //             resource2.close();
          +     *     //         }
          +     *     //     }
          +     *     //     catch ( IOException e )
          +     *     //     {
          +     *     //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          +     *     //         propagate instead of this one.
          +     *     //     }
          +     *     // }
          +     * }
          +     * 
          + *

          + * + * @param writer The writer to close or {@code null}. + * @deprecated use try-with-resources + */ + @Deprecated +/** + * Closes a {@code Writer} suppressing any {@code IOException}. + *

          + * Note: The use case justifying this method is a shortcoming of the Java language up to but not including + * Java 7. For any code targeting Java 7 or later use of this method is highly discouraged and the + * {@code try-with-resources} statement should be used instead. Care must be taken to not use this method in a way + * {@code IOException}s get suppressed incorrectly. + * You must close all resources in use inside the {@code try} block to not suppress exceptions in the + * {@code finally} block incorrectly by using this method. + *

          + *

          + * Example:
          + *

          + * // Introduce variables for the resources and initialize them to null. This cannot throw an exception.
          + * Closeable resource1 = null;
          + * Closeable resource2 = null;
          + * try
          + * {
          + * // Obtain a resource object and assign it to variable resource1. This may throw an exception.
          + * // If successful, resource1 != null.
          + * resource1 = ...
          + *
          + * // Obtain a resource object and assign it to variable resource2. This may throw an exception.
          + * // If successful, resource2 != null. Not reached if an exception has been thrown above.
          + * resource2 = ...
          + *
          + * // Perform operations on the resources. This may throw an exception. Not reached if an exception has been
          + * // thrown above. Note: Treat the variables resource1 and resource2 the same way as if they would have been
          + * // declared with the final modifier - that is - do NOT write anyting like resource1 = something else or
          + * // resource2 = something else here.
          + * resource1 ...
          + * resource2 ...
          + *
          + * // Finally, close the resources and set the variables to null indicating successful completion.
          + * // This may throw an exception. Not reached if an exception has been thrown above.
          + * resource1.close();
          + * resource1 = null;
          + * // This may throw an exception. Not reached if an exception has been thrown above.
          + * resource2.close();
          + * resource2 = null;
          + *
          + * // All resources are closed at this point and all operations (up to here) completed successfully without
          + * // throwing an exception we would need to handle (by letting it propagate or by catching and handling it).
          + * }
          + * finally
          + * {
          + * // Cleanup any resource not closed in the try block due to an exception having been thrown and suppress any
          + * // exception this may produce to not stop the exception from the try block to be propagated. If the try
          + * // block completed successfully, all variables will have been set to null there and this will not do
          + * // anything. This is just to cleanup properly in case of an exception.
          + *
          + * IOUtil.close( resource1 );
          + * IOUtil.close( resource2 );
          + *
          + * // Without that utility method you would need to write the following:
          + * //
          + * // try
          + * // {
          + * //     if ( resource1 != null )
          + * //     {
          + * //         resource1.close();
          + * //     }
          + * // }
          + * // catch( IOException e )
          + * // {
          + * //     Suppressed. If resource1 != null, an exception has already been thrown in the try block we need to
          + * //     propagate instead of this one.
          + * // }
          + * // finally
          + * // {
          + * //     try
          + * //     {
          + * //         if ( resource2 != null )
          + * //         {
          + * //             resource2.close();
          + * //         }
          + * //     }
          + * //     catch ( IOException e )
          + * //     {
          + * //         Suppressed. If resource2 != null, an exception has already been thrown in the try block we need to
          + * //         propagate instead of this one.
          + * //     }
          + * // }
          + * }
          + * 
          + *

          + * + * @param writer + * The writer to close or {@code null}. + * @deprecated use try-with-resources + */ +public static void close(@javax.annotation.Nullable +java.io.Writer writer) { + try { + { + /* NPEX_NULL_EXP */ + writer.close(); + } + } catch (java.io.IOException ex) { + // Suppressed + } +} +} diff --git a/Java/maven-shared-utils-IOUtil_1358/metadata.json b/Java/maven-shared-utils-IOUtil_1358/metadata.json new file mode 100644 index 000000000..ec3008ee3 --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1358/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-IOUtil_1358", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/io/IOUtil.java", + "line": 1450, + "npe_method": "close", + "deref_field": "writer", + "npe_class": "IOUtil", + "repo": "maven-shared-utils", + "bug_id": "IOUtil_1358" + } +} diff --git a/Java/maven-shared-utils-IOUtil_1358/npe.json b/Java/maven-shared-utils-IOUtil_1358/npe.json new file mode 100644 index 000000000..03d33ae7d --- /dev/null +++ b/Java/maven-shared-utils-IOUtil_1358/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/io/IOUtil.java", + "line": 1450, + "npe_method": "close", + "deref_field": "writer", + "npe_class": "IOUtil" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Os_311/Dockerfile b/Java/maven-shared-utils-Os_311/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Os_311/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Os_311/buggy.java b/Java/maven-shared-utils-Os_311/buggy.java new file mode 100644 index 000000000..df8d1cdf7 --- /dev/null +++ b/Java/maven-shared-utils-Os_311/buggy.java @@ -0,0 +1,406 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + *

          Condition that tests the OS type.

          + *

          + *

          This class got copied over from Apache ANT. + * Even the version from plexus-utils was + * only an ANT fork!
          + * The last time it got copied was on 2011-08-12

          + *

          + *

          When merging changes please take care of the special + * OS_FAMILY handling in this version of Os.java!

          + * + * @author Stefan Bodewig + * @author Magesh Umasankar + * @author Brian Fox + * @author Mark Struberg + * + */ +public class Os +{ + /** + * The OS Name. + */ + public static final String OS_NAME = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OA architecture. + */ + public static final String OS_ARCH = System.getProperty( "os.arch" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OS version. + */ + public static final String OS_VERSION = System.getProperty( "os.version" ).toLowerCase( Locale.ENGLISH ); + + /** + * The path separator. + */ + public static final String PATH_SEP = System.getProperty( "path.separator" ); + + /** + * system line separator , e.g. "\n" on unixoid systems and "\r\n" on Windows + */ + public static final String LINE_SEP = System.getProperty( "line.separator" ); + + /** + * OS Family + */ + public static final String OS_FAMILY = getOsFamily(); + + // store the valid families + private static final Set VALID_FAMILIES = getValidFamilies(); + + + /** + * OS family to look for + */ + private String family; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WIN9X = "win9x"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OPENVMS = "openvms"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * + * @see bugzilla issue + * @see HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + + /** + * The set of valid families. This methods initializes the set until + * VALID_FAMILIES constant is set. + * @return The set of families. + */ + public static Set getValidFamilies() + { + if ( VALID_FAMILIES != null ) + { + return VALID_FAMILIES; + } + + Set valid = new HashSet(); + valid.add( FAMILY_DOS ); + valid.add( FAMILY_MAC ); + valid.add( FAMILY_NETWARE ); + valid.add( FAMILY_NT ); + valid.add( FAMILY_OPENVMS ); + valid.add( FAMILY_OS2 ); + valid.add( FAMILY_OS400 ); + valid.add( FAMILY_TANDEM ); + valid.add( FAMILY_UNIX ); + valid.add( FAMILY_WIN9X ); + valid.add( FAMILY_WINDOWS ); + valid.add( FAMILY_ZOS ); + + return Collections.unmodifiableSet( valid ); + } + + /** + * Default constructor + */ + public Os() + { + //default + } + + /** + * Constructor that sets the family attribute + * + * @param family a String value + */ + public Os( String family ) + { + setFamily( family ); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
          + * Possible values:
          + *
            + *
          • dos
          • + *
          • mac
          • + *
          • netware
          • + *
          • os/2
          • + *
          • tandem
          • + *
          • unix
          • + *
          • windows
          • + *
          • win9x
          • + *
          • z/os
          • + *
          • os/400
          • + *
          + */ + private void setFamily( String f ) + { + family = f.toLowerCase( Locale.ENGLISH ); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * + * @return true if the os matches. + * @see Os#setFamily(String) + */ + boolean eval() + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * + * @param family the family to check for + * @return true if the OS matches + * + */ + public static boolean isFamily( String family ) + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * + */ + public static boolean isName( String name ) + { + return isOs( null, name, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * + */ + public static boolean isArch( String arch ) + { + return isOs( null, null, arch, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * + */ + public static boolean isVersion( String version ) + { + return isOs( null, null, null, version ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * + */ +/** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family + * The OS family + * @param name + * The OS name + * @param arch + * The OS architecture + * @param version + * The OS version + * @return true if the OS matches + */ +private static boolean isOs(java.lang.String family, java.lang.String name, java.lang.String arch, java.lang.String version) { + boolean retValue = false; + if ((((family != null) || (name != null)) || (arch != null)) || (version != null)) { + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + { + // windows probing logic relies on the word 'windows' in + // the OS + boolean isWindows = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_WINDOWS); + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + // there are only four 9x platforms that we look for + is9x = ((org.apache.maven.shared.utils.Os.OS_NAME.contains("95") || org.apache.maven.shared.utils.Os.OS_NAME.contains("98")) || org.apache.maven.shared.utils.Os.OS_NAME.contains("me")) || // wince isn't really 9x, but crippled enough to + // be a muchness. Ant doesnt run on CE, anyway. + org.apache.maven.shared.utils.Os.OS_NAME.contains("ce"); + isNT = !is9x; + } + if (/* NPEX_NULL_EXP */ + family.equals(org.apache.maven.shared.utils.Os.FAMILY_WINDOWS)) { + isFamily = isWindows; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_WIN9X)) { + isFamily = isWindows && is9x; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_NT)) { + isFamily = isWindows && isNT; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OS2)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OS2); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_NETWARE)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_NETWARE); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_DOS)) { + isFamily = org.apache.maven.shared.utils.Os.PATH_SEP.equals(";") && (!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_NETWARE)); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_MAC)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_MAC) || org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.DARWIN); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_TANDEM)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains("nonstop_kernel"); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_UNIX)) { + isFamily = (org.apache.maven.shared.utils.Os.PATH_SEP.equals(":") && (!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS))) && (((!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_MAC)) || org.apache.maven.shared.utils.Os.OS_NAME.endsWith("x")) || org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.DARWIN)); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_ZOS)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_ZOS) || org.apache.maven.shared.utils.Os.OS_NAME.contains("os/390"); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OS400)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OS400); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS); + } else { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(family.toLowerCase(java.util.Locale.US)); + } + } + if (name != null) { + isName = name.equals(org.apache.maven.shared.utils.Os.OS_NAME); + } + if (arch != null) { + isArch = arch.equals(org.apache.maven.shared.utils.Os.OS_ARCH); + } + if (version != null) { + isVersion = version.equals(org.apache.maven.shared.utils.Os.OS_VERSION); + } + retValue = ((isFamily && isName) && isArch) && isVersion; + } + return retValue; +} + + /** + * Helper method to determine the current OS family. + * + * @return name of current OS family. + */ + private static String getOsFamily() + { + Set families = getValidFamilies(); + + for ( String fam : families ) + { + if ( Os.isFamily( fam ) ) + { + return fam; + } + } + return null; + } + + /** + * Test if the given family String represents a valid Family + * + * @param family the os family + * @return true if 'family' represents a valid OS-Family, false otherwise. + */ + public static boolean isValidFamily( String family ) + { + return VALID_FAMILIES.contains( family ); + } + +} diff --git a/Java/maven-shared-utils-Os_311/metadata.json b/Java/maven-shared-utils-Os_311/metadata.json new file mode 100644 index 000000000..60f625969 --- /dev/null +++ b/Java/maven-shared-utils-Os_311/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Os_311", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/Os.java", + "line": 334, + "npe_method": "isOs", + "deref_field": "family", + "npe_class": "Os", + "repo": "maven-shared-utils", + "bug_id": "Os_311" + } +} diff --git a/Java/maven-shared-utils-Os_311/npe.json b/Java/maven-shared-utils-Os_311/npe.json new file mode 100644 index 000000000..7d136030a --- /dev/null +++ b/Java/maven-shared-utils-Os_311/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/Os.java", + "line": 334, + "npe_method": "isOs", + "deref_field": "family", + "npe_class": "Os" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Os_383/Dockerfile b/Java/maven-shared-utils-Os_383/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Os_383/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Os_383/buggy.java b/Java/maven-shared-utils-Os_383/buggy.java new file mode 100644 index 000000000..17f488991 --- /dev/null +++ b/Java/maven-shared-utils-Os_383/buggy.java @@ -0,0 +1,406 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + *

          Condition that tests the OS type.

          + *

          + *

          This class got copied over from Apache ANT. + * Even the version from plexus-utils was + * only an ANT fork!
          + * The last time it got copied was on 2011-08-12

          + *

          + *

          When merging changes please take care of the special + * OS_FAMILY handling in this version of Os.java!

          + * + * @author Stefan Bodewig + * @author Magesh Umasankar + * @author Brian Fox + * @author Mark Struberg + * + */ +public class Os +{ + /** + * The OS Name. + */ + public static final String OS_NAME = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OA architecture. + */ + public static final String OS_ARCH = System.getProperty( "os.arch" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OS version. + */ + public static final String OS_VERSION = System.getProperty( "os.version" ).toLowerCase( Locale.ENGLISH ); + + /** + * The path separator. + */ + public static final String PATH_SEP = System.getProperty( "path.separator" ); + + /** + * system line separator , e.g. "\n" on unixoid systems and "\r\n" on Windows + */ + public static final String LINE_SEP = System.getProperty( "line.separator" ); + + /** + * OS Family + */ + public static final String OS_FAMILY = getOsFamily(); + + // store the valid families + private static final Set VALID_FAMILIES = getValidFamilies(); + + + /** + * OS family to look for + */ + private String family; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WIN9X = "win9x"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OPENVMS = "openvms"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * + * @see bugzilla issue + * @see HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + + /** + * The set of valid families. This methods initializes the set until + * VALID_FAMILIES constant is set. + * @return The set of families. + */ + public static Set getValidFamilies() + { + if ( VALID_FAMILIES != null ) + { + return VALID_FAMILIES; + } + + Set valid = new HashSet(); + valid.add( FAMILY_DOS ); + valid.add( FAMILY_MAC ); + valid.add( FAMILY_NETWARE ); + valid.add( FAMILY_NT ); + valid.add( FAMILY_OPENVMS ); + valid.add( FAMILY_OS2 ); + valid.add( FAMILY_OS400 ); + valid.add( FAMILY_TANDEM ); + valid.add( FAMILY_UNIX ); + valid.add( FAMILY_WIN9X ); + valid.add( FAMILY_WINDOWS ); + valid.add( FAMILY_ZOS ); + + return Collections.unmodifiableSet( valid ); + } + + /** + * Default constructor + */ + public Os() + { + //default + } + + /** + * Constructor that sets the family attribute + * + * @param family a String value + */ + public Os( String family ) + { + setFamily( family ); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
          + * Possible values:
          + *
            + *
          • dos
          • + *
          • mac
          • + *
          • netware
          • + *
          • os/2
          • + *
          • tandem
          • + *
          • unix
          • + *
          • windows
          • + *
          • win9x
          • + *
          • z/os
          • + *
          • os/400
          • + *
          + */ + private void setFamily( String f ) + { + family = f.toLowerCase( Locale.ENGLISH ); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * + * @return true if the os matches. + * @see Os#setFamily(String) + */ + boolean eval() + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * + * @param family the family to check for + * @return true if the OS matches + * + */ + public static boolean isFamily( String family ) + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * + */ + public static boolean isName( String name ) + { + return isOs( null, name, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * + */ + public static boolean isArch( String arch ) + { + return isOs( null, null, arch, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * + */ + public static boolean isVersion( String version ) + { + return isOs( null, null, null, version ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * + */ +/** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family + * The OS family + * @param name + * The OS name + * @param arch + * The OS architecture + * @param version + * The OS version + * @return true if the OS matches + */ +private static boolean isOs(java.lang.String family, java.lang.String name, java.lang.String arch, java.lang.String version) { + boolean retValue = false; + if ((((family != null) || (name != null)) || (arch != null)) || (version != null)) { + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + if (family != null) { + // windows probing logic relies on the word 'windows' in + // the OS + boolean isWindows = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_WINDOWS); + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + // there are only four 9x platforms that we look for + is9x = ((org.apache.maven.shared.utils.Os.OS_NAME.contains("95") || org.apache.maven.shared.utils.Os.OS_NAME.contains("98")) || org.apache.maven.shared.utils.Os.OS_NAME.contains("me")) || // wince isn't really 9x, but crippled enough to + // be a muchness. Ant doesnt run on CE, anyway. + org.apache.maven.shared.utils.Os.OS_NAME.contains("ce"); + isNT = !is9x; + } + if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_WINDOWS)) { + isFamily = isWindows; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_WIN9X)) { + isFamily = isWindows && is9x; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_NT)) { + isFamily = isWindows && isNT; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OS2)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OS2); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_NETWARE)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_NETWARE); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_DOS)) { + isFamily = org.apache.maven.shared.utils.Os.PATH_SEP.equals(";") && (!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_NETWARE)); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_MAC)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_MAC) || org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.DARWIN); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_TANDEM)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains("nonstop_kernel"); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_UNIX)) { + isFamily = (org.apache.maven.shared.utils.Os.PATH_SEP.equals(":") && (!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS))) && (((!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_MAC)) || org.apache.maven.shared.utils.Os.OS_NAME.endsWith("x")) || org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.DARWIN)); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_ZOS)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_ZOS) || org.apache.maven.shared.utils.Os.OS_NAME.contains("os/390"); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OS400)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OS400); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS); + } else { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(family.toLowerCase(java.util.Locale.US)); + } + } + { + isName = /* NPEX_NULL_EXP */ + name.equals(org.apache.maven.shared.utils.Os.OS_NAME); + } + if (arch != null) { + isArch = arch.equals(org.apache.maven.shared.utils.Os.OS_ARCH); + } + if (version != null) { + isVersion = version.equals(org.apache.maven.shared.utils.Os.OS_VERSION); + } + retValue = ((isFamily && isName) && isArch) && isVersion; + } + return retValue; +} + + /** + * Helper method to determine the current OS family. + * + * @return name of current OS family. + */ + private static String getOsFamily() + { + Set families = getValidFamilies(); + + for ( String fam : families ) + { + if ( Os.isFamily( fam ) ) + { + return fam; + } + } + return null; + } + + /** + * Test if the given family String represents a valid Family + * + * @param family the os family + * @return true if 'family' represents a valid OS-Family, false otherwise. + */ + public static boolean isValidFamily( String family ) + { + return VALID_FAMILIES.contains( family ); + } + +} diff --git a/Java/maven-shared-utils-Os_383/metadata.json b/Java/maven-shared-utils-Os_383/metadata.json new file mode 100644 index 000000000..3daf4007d --- /dev/null +++ b/Java/maven-shared-utils-Os_383/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Os_383", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/Os.java", + "line": 363, + "npe_method": "isOs", + "deref_field": "name", + "npe_class": "Os", + "repo": "maven-shared-utils", + "bug_id": "Os_383" + } +} diff --git a/Java/maven-shared-utils-Os_383/npe.json b/Java/maven-shared-utils-Os_383/npe.json new file mode 100644 index 000000000..50d3b72f1 --- /dev/null +++ b/Java/maven-shared-utils-Os_383/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/Os.java", + "line": 363, + "npe_method": "isOs", + "deref_field": "name", + "npe_class": "Os" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Os_387/Dockerfile b/Java/maven-shared-utils-Os_387/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Os_387/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Os_387/buggy.java b/Java/maven-shared-utils-Os_387/buggy.java new file mode 100644 index 000000000..a3ff8a95f --- /dev/null +++ b/Java/maven-shared-utils-Os_387/buggy.java @@ -0,0 +1,406 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + *

          Condition that tests the OS type.

          + *

          + *

          This class got copied over from Apache ANT. + * Even the version from plexus-utils was + * only an ANT fork!
          + * The last time it got copied was on 2011-08-12

          + *

          + *

          When merging changes please take care of the special + * OS_FAMILY handling in this version of Os.java!

          + * + * @author Stefan Bodewig + * @author Magesh Umasankar + * @author Brian Fox + * @author Mark Struberg + * + */ +public class Os +{ + /** + * The OS Name. + */ + public static final String OS_NAME = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OA architecture. + */ + public static final String OS_ARCH = System.getProperty( "os.arch" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OS version. + */ + public static final String OS_VERSION = System.getProperty( "os.version" ).toLowerCase( Locale.ENGLISH ); + + /** + * The path separator. + */ + public static final String PATH_SEP = System.getProperty( "path.separator" ); + + /** + * system line separator , e.g. "\n" on unixoid systems and "\r\n" on Windows + */ + public static final String LINE_SEP = System.getProperty( "line.separator" ); + + /** + * OS Family + */ + public static final String OS_FAMILY = getOsFamily(); + + // store the valid families + private static final Set VALID_FAMILIES = getValidFamilies(); + + + /** + * OS family to look for + */ + private String family; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WIN9X = "win9x"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OPENVMS = "openvms"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * + * @see bugzilla issue + * @see HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + + /** + * The set of valid families. This methods initializes the set until + * VALID_FAMILIES constant is set. + * @return The set of families. + */ + public static Set getValidFamilies() + { + if ( VALID_FAMILIES != null ) + { + return VALID_FAMILIES; + } + + Set valid = new HashSet(); + valid.add( FAMILY_DOS ); + valid.add( FAMILY_MAC ); + valid.add( FAMILY_NETWARE ); + valid.add( FAMILY_NT ); + valid.add( FAMILY_OPENVMS ); + valid.add( FAMILY_OS2 ); + valid.add( FAMILY_OS400 ); + valid.add( FAMILY_TANDEM ); + valid.add( FAMILY_UNIX ); + valid.add( FAMILY_WIN9X ); + valid.add( FAMILY_WINDOWS ); + valid.add( FAMILY_ZOS ); + + return Collections.unmodifiableSet( valid ); + } + + /** + * Default constructor + */ + public Os() + { + //default + } + + /** + * Constructor that sets the family attribute + * + * @param family a String value + */ + public Os( String family ) + { + setFamily( family ); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
          + * Possible values:
          + *
            + *
          • dos
          • + *
          • mac
          • + *
          • netware
          • + *
          • os/2
          • + *
          • tandem
          • + *
          • unix
          • + *
          • windows
          • + *
          • win9x
          • + *
          • z/os
          • + *
          • os/400
          • + *
          + */ + private void setFamily( String f ) + { + family = f.toLowerCase( Locale.ENGLISH ); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * + * @return true if the os matches. + * @see Os#setFamily(String) + */ + boolean eval() + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * + * @param family the family to check for + * @return true if the OS matches + * + */ + public static boolean isFamily( String family ) + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * + */ + public static boolean isName( String name ) + { + return isOs( null, name, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * + */ + public static boolean isArch( String arch ) + { + return isOs( null, null, arch, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * + */ + public static boolean isVersion( String version ) + { + return isOs( null, null, null, version ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * + */ +/** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family + * The OS family + * @param name + * The OS name + * @param arch + * The OS architecture + * @param version + * The OS version + * @return true if the OS matches + */ +private static boolean isOs(java.lang.String family, java.lang.String name, java.lang.String arch, java.lang.String version) { + boolean retValue = false; + if ((((family != null) || (name != null)) || (arch != null)) || (version != null)) { + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + if (family != null) { + // windows probing logic relies on the word 'windows' in + // the OS + boolean isWindows = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_WINDOWS); + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + // there are only four 9x platforms that we look for + is9x = ((org.apache.maven.shared.utils.Os.OS_NAME.contains("95") || org.apache.maven.shared.utils.Os.OS_NAME.contains("98")) || org.apache.maven.shared.utils.Os.OS_NAME.contains("me")) || // wince isn't really 9x, but crippled enough to + // be a muchness. Ant doesnt run on CE, anyway. + org.apache.maven.shared.utils.Os.OS_NAME.contains("ce"); + isNT = !is9x; + } + if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_WINDOWS)) { + isFamily = isWindows; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_WIN9X)) { + isFamily = isWindows && is9x; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_NT)) { + isFamily = isWindows && isNT; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OS2)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OS2); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_NETWARE)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_NETWARE); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_DOS)) { + isFamily = org.apache.maven.shared.utils.Os.PATH_SEP.equals(";") && (!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_NETWARE)); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_MAC)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_MAC) || org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.DARWIN); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_TANDEM)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains("nonstop_kernel"); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_UNIX)) { + isFamily = (org.apache.maven.shared.utils.Os.PATH_SEP.equals(":") && (!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS))) && (((!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_MAC)) || org.apache.maven.shared.utils.Os.OS_NAME.endsWith("x")) || org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.DARWIN)); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_ZOS)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_ZOS) || org.apache.maven.shared.utils.Os.OS_NAME.contains("os/390"); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OS400)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OS400); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS); + } else { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(family.toLowerCase(java.util.Locale.US)); + } + } + if (name != null) { + isName = name.equals(org.apache.maven.shared.utils.Os.OS_NAME); + } + { + isArch = /* NPEX_NULL_EXP */ + arch.equals(org.apache.maven.shared.utils.Os.OS_ARCH); + } + if (version != null) { + isVersion = version.equals(org.apache.maven.shared.utils.Os.OS_VERSION); + } + retValue = ((isFamily && isName) && isArch) && isVersion; + } + return retValue; +} + + /** + * Helper method to determine the current OS family. + * + * @return name of current OS family. + */ + private static String getOsFamily() + { + Set families = getValidFamilies(); + + for ( String fam : families ) + { + if ( Os.isFamily( fam ) ) + { + return fam; + } + } + return null; + } + + /** + * Test if the given family String represents a valid Family + * + * @param family the os family + * @return true if 'family' represents a valid OS-Family, false otherwise. + */ + public static boolean isValidFamily( String family ) + { + return VALID_FAMILIES.contains( family ); + } + +} diff --git a/Java/maven-shared-utils-Os_387/metadata.json b/Java/maven-shared-utils-Os_387/metadata.json new file mode 100644 index 000000000..865fabb2e --- /dev/null +++ b/Java/maven-shared-utils-Os_387/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Os_387", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/Os.java", + "line": 366, + "npe_method": "isOs", + "deref_field": "arch", + "npe_class": "Os", + "repo": "maven-shared-utils", + "bug_id": "Os_387" + } +} diff --git a/Java/maven-shared-utils-Os_387/npe.json b/Java/maven-shared-utils-Os_387/npe.json new file mode 100644 index 000000000..8bb4e312b --- /dev/null +++ b/Java/maven-shared-utils-Os_387/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/Os.java", + "line": 366, + "npe_method": "isOs", + "deref_field": "arch", + "npe_class": "Os" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Os_391/Dockerfile b/Java/maven-shared-utils-Os_391/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Os_391/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Os_391/buggy.java b/Java/maven-shared-utils-Os_391/buggy.java new file mode 100644 index 000000000..4cc0751e1 --- /dev/null +++ b/Java/maven-shared-utils-Os_391/buggy.java @@ -0,0 +1,406 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + *

          Condition that tests the OS type.

          + *

          + *

          This class got copied over from Apache ANT. + * Even the version from plexus-utils was + * only an ANT fork!
          + * The last time it got copied was on 2011-08-12

          + *

          + *

          When merging changes please take care of the special + * OS_FAMILY handling in this version of Os.java!

          + * + * @author Stefan Bodewig + * @author Magesh Umasankar + * @author Brian Fox + * @author Mark Struberg + * + */ +public class Os +{ + /** + * The OS Name. + */ + public static final String OS_NAME = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OA architecture. + */ + public static final String OS_ARCH = System.getProperty( "os.arch" ).toLowerCase( Locale.ENGLISH ); + + /** + * The OS version. + */ + public static final String OS_VERSION = System.getProperty( "os.version" ).toLowerCase( Locale.ENGLISH ); + + /** + * The path separator. + */ + public static final String PATH_SEP = System.getProperty( "path.separator" ); + + /** + * system line separator , e.g. "\n" on unixoid systems and "\r\n" on Windows + */ + public static final String LINE_SEP = System.getProperty( "line.separator" ); + + /** + * OS Family + */ + public static final String OS_FAMILY = getOsFamily(); + + // store the valid families + private static final Set VALID_FAMILIES = getValidFamilies(); + + + /** + * OS family to look for + */ + private String family; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WIN9X = "win9x"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OPENVMS = "openvms"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * + * @see bugzilla issue + * @see HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + + /** + * The set of valid families. This methods initializes the set until + * VALID_FAMILIES constant is set. + * @return The set of families. + */ + public static Set getValidFamilies() + { + if ( VALID_FAMILIES != null ) + { + return VALID_FAMILIES; + } + + Set valid = new HashSet(); + valid.add( FAMILY_DOS ); + valid.add( FAMILY_MAC ); + valid.add( FAMILY_NETWARE ); + valid.add( FAMILY_NT ); + valid.add( FAMILY_OPENVMS ); + valid.add( FAMILY_OS2 ); + valid.add( FAMILY_OS400 ); + valid.add( FAMILY_TANDEM ); + valid.add( FAMILY_UNIX ); + valid.add( FAMILY_WIN9X ); + valid.add( FAMILY_WINDOWS ); + valid.add( FAMILY_ZOS ); + + return Collections.unmodifiableSet( valid ); + } + + /** + * Default constructor + */ + public Os() + { + //default + } + + /** + * Constructor that sets the family attribute + * + * @param family a String value + */ + public Os( String family ) + { + setFamily( family ); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
          + * Possible values:
          + *
            + *
          • dos
          • + *
          • mac
          • + *
          • netware
          • + *
          • os/2
          • + *
          • tandem
          • + *
          • unix
          • + *
          • windows
          • + *
          • win9x
          • + *
          • z/os
          • + *
          • os/400
          • + *
          + */ + private void setFamily( String f ) + { + family = f.toLowerCase( Locale.ENGLISH ); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * + * @return true if the os matches. + * @see Os#setFamily(String) + */ + boolean eval() + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * + * @param family the family to check for + * @return true if the OS matches + * + */ + public static boolean isFamily( String family ) + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * + */ + public static boolean isName( String name ) + { + return isOs( null, name, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * + */ + public static boolean isArch( String arch ) + { + return isOs( null, null, arch, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * + */ + public static boolean isVersion( String version ) + { + return isOs( null, null, null, version ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * + */ +/** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family + * The OS family + * @param name + * The OS name + * @param arch + * The OS architecture + * @param version + * The OS version + * @return true if the OS matches + */ +private static boolean isOs(java.lang.String family, java.lang.String name, java.lang.String arch, java.lang.String version) { + boolean retValue = false; + if ((((family != null) || (name != null)) || (arch != null)) || (version != null)) { + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + if (family != null) { + // windows probing logic relies on the word 'windows' in + // the OS + boolean isWindows = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_WINDOWS); + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + // there are only four 9x platforms that we look for + is9x = ((org.apache.maven.shared.utils.Os.OS_NAME.contains("95") || org.apache.maven.shared.utils.Os.OS_NAME.contains("98")) || org.apache.maven.shared.utils.Os.OS_NAME.contains("me")) || // wince isn't really 9x, but crippled enough to + // be a muchness. Ant doesnt run on CE, anyway. + org.apache.maven.shared.utils.Os.OS_NAME.contains("ce"); + isNT = !is9x; + } + if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_WINDOWS)) { + isFamily = isWindows; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_WIN9X)) { + isFamily = isWindows && is9x; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_NT)) { + isFamily = isWindows && isNT; + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OS2)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OS2); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_NETWARE)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_NETWARE); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_DOS)) { + isFamily = org.apache.maven.shared.utils.Os.PATH_SEP.equals(";") && (!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_NETWARE)); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_MAC)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_MAC) || org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.DARWIN); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_TANDEM)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains("nonstop_kernel"); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_UNIX)) { + isFamily = (org.apache.maven.shared.utils.Os.PATH_SEP.equals(":") && (!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS))) && (((!org.apache.maven.shared.utils.Os.isFamily(org.apache.maven.shared.utils.Os.FAMILY_MAC)) || org.apache.maven.shared.utils.Os.OS_NAME.endsWith("x")) || org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.DARWIN)); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_ZOS)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_ZOS) || org.apache.maven.shared.utils.Os.OS_NAME.contains("os/390"); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OS400)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OS400); + } else if (family.equals(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS)) { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(org.apache.maven.shared.utils.Os.FAMILY_OPENVMS); + } else { + isFamily = org.apache.maven.shared.utils.Os.OS_NAME.contains(family.toLowerCase(java.util.Locale.US)); + } + } + if (name != null) { + isName = name.equals(org.apache.maven.shared.utils.Os.OS_NAME); + } + if (arch != null) { + isArch = arch.equals(org.apache.maven.shared.utils.Os.OS_ARCH); + } + { + isVersion = /* NPEX_NULL_EXP */ + version.equals(org.apache.maven.shared.utils.Os.OS_VERSION); + } + retValue = ((isFamily && isName) && isArch) && isVersion; + } + return retValue; +} + + /** + * Helper method to determine the current OS family. + * + * @return name of current OS family. + */ + private static String getOsFamily() + { + Set families = getValidFamilies(); + + for ( String fam : families ) + { + if ( Os.isFamily( fam ) ) + { + return fam; + } + } + return null; + } + + /** + * Test if the given family String represents a valid Family + * + * @param family the os family + * @return true if 'family' represents a valid OS-Family, false otherwise. + */ + public static boolean isValidFamily( String family ) + { + return VALID_FAMILIES.contains( family ); + } + +} diff --git a/Java/maven-shared-utils-Os_391/metadata.json b/Java/maven-shared-utils-Os_391/metadata.json new file mode 100644 index 000000000..70cb28a58 --- /dev/null +++ b/Java/maven-shared-utils-Os_391/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Os_391", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/Os.java", + "line": 369, + "npe_method": "isOs", + "deref_field": "version", + "npe_class": "Os", + "repo": "maven-shared-utils", + "bug_id": "Os_391" + } +} diff --git a/Java/maven-shared-utils-Os_391/npe.json b/Java/maven-shared-utils-Os_391/npe.json new file mode 100644 index 000000000..79c006d12 --- /dev/null +++ b/Java/maven-shared-utils-Os_391/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/Os.java", + "line": 369, + "npe_method": "isOs", + "deref_field": "version", + "npe_class": "Os" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-PathTool_270/Dockerfile b/Java/maven-shared-utils-PathTool_270/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-PathTool_270/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-PathTool_270/buggy.java b/Java/maven-shared-utils-PathTool_270/buggy.java new file mode 100644 index 000000000..b8674a8ab --- /dev/null +++ b/Java/maven-shared-utils-PathTool_270/buggy.java @@ -0,0 +1,360 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Path tool contains static methods to assist in determining path-related + * information such as relative paths. + *

          + * This class originally got developed at Apache Anakia and later maintained + * in maven-utils of Apache Maven-1. + * Some external fixes by Apache Committers have been applied later. + */ +public class PathTool +{ + + /** + * The constructor. + * + * @deprecated This is a utility class with only static methods. Don't create instances of it. + */ + @Deprecated + public PathTool() + { + } + + /** + * Determines the relative path of a filename from a base directory. + * This method is useful in building relative links within pages of + * a web site. It provides similar functionality to Anakia's + * $relativePath context variable. The arguments to + * this method may contain either forward or backward slashes as + * file separators. The relative path returned is formed using + * forward slashes as it is expected this path is to be used as a + * link in a web page (again mimicking Anakia's behavior). + *

          + * This method is thread-safe. + *
          + *

          +     * PathTool.getRelativePath( null, null )                                   = ""
          +     * PathTool.getRelativePath( null, "/usr/local/java/bin" )                  = ""
          +     * PathTool.getRelativePath( "/usr/local/", null )                          = ""
          +     * PathTool.getRelativePath( "/usr/local/", "/usr/local/java/bin" )         = ".."
          +     * PathTool.getRelativePath( "/usr/local/", "/usr/local/java/bin/java.sh" ) = "../.."
          +     * PathTool.getRelativePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) = ""
          +     * 
          + * + * @param basedir The base directory. + * @param filename The filename that is relative to the base + * directory. + * @return The relative path of the filename from the base + * directory. This value is not terminated with a forward slash. + * A zero-length string is returned if: the filename is not relative to + * the base directory, basedir is null or zero-length, + * or filename is null or zero-length. + */ + public static String getRelativePath( @Nullable String basedir, @Nullable String filename ) + { + basedir = uppercaseDrive( basedir ); + filename = uppercaseDrive( filename ); + + /* + * Verify the arguments and make sure the filename is relative + * to the base directory. + */ + if ( basedir == null || basedir.length() == 0 || filename == null || filename.length() == 0 + || !filename.startsWith( basedir ) ) + { + return ""; + } + + /* + * Normalize the arguments. First, determine the file separator + * that is being used, then strip that off the end of both the + * base directory and filename. + */ + String separator = determineSeparator( filename ); + basedir = StringUtils.chompLast( basedir, separator ); + filename = StringUtils.chompLast( filename, separator ); + + /* + * Remove the base directory from the filename to end up with a + * relative filename (relative to the base directory). This + * filename is then used to determine the relative path. + */ + String relativeFilename = filename.substring( basedir.length() ); + + return determineRelativePath( relativeFilename, separator ); + } + + /** + * This method can calculate the relative path between two pathes on a file system. + *
          + *
          +     * PathTool.getRelativeFilePath( null, null )                                   = ""
          +     * PathTool.getRelativeFilePath( null, "/usr/local/java/bin" )                  = ""
          +     * PathTool.getRelativeFilePath( "/usr/local", null )                           = ""
          +     * PathTool.getRelativeFilePath( "/usr/local", "/usr/local/java/bin" )          = "java/bin"
          +     * PathTool.getRelativeFilePath( "/usr/local", "/usr/local/java/bin/" )         = "java/bin"
          +     * PathTool.getRelativeFilePath( "/usr/local/java/bin", "/usr/local/" )         = "../.."
          +     * PathTool.getRelativeFilePath( "/usr/local/", "/usr/local/java/bin/java.sh" ) = "java/bin/java.sh"
          +     * PathTool.getRelativeFilePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) = "../../.."
          +     * PathTool.getRelativeFilePath( "/usr/local/", "/bin" )                        = "../../bin"
          +     * PathTool.getRelativeFilePath( "/bin", "/usr/local/" )                        = "../usr/local"
          +     * 
          + * Note: On Windows based system, the / character should be replaced by \ character. + * + * @param oldPath old path + * @param newPath new path + * @return a relative file path from oldPath. + */ + public static String getRelativeFilePath( final String oldPath, final String newPath ) + { + if ( StringUtils.isEmpty( oldPath ) || StringUtils.isEmpty( newPath ) ) + { + return ""; + } + + // normalise the path delimiters + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they dont match, no + // relative path + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + // one has a drive path element and the other doesnt, no relative + // path. + return null; + } + + String resultPath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + if ( newPath.endsWith( File.separator ) && !resultPath.endsWith( File.separator ) ) + { + return resultPath + File.separator; + } + + return resultPath; + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * Determines the relative path of a filename. For each separator + * within the filename (except the leading if present), append the + * "../" string to the return value. + * + * @param filename The filename to parse. + * @param separator The separator used within the filename. + * @return The relative path of the filename. This value is not + * terminated with a forward slash. A zero-length string is + * returned if: the filename is zero-length. + */ + @Nonnull private static String determineRelativePath( @Nonnull String filename, @Nonnull String separator ) + { + if ( filename.length() == 0 ) + { + return ""; + } + + /* + * Count the slashes in the relative filename, but exclude the + * leading slash. If the path has no slashes, then the filename + * is relative to the current directory. + */ + int slashCount = StringUtils.countMatches( filename, separator ) - 1; + if ( slashCount <= 0 ) + { + return "."; + } + + /* + * The relative filename contains one or more slashes indicating + * that the file is within one or more directories. Thus, each + * slash represents a "../" in the relative path. + */ + StringBuilder sb = new StringBuilder(); + for ( int i = 0; i < slashCount; i++ ) + { + sb.append( "../" ); + } + + /* + * Finally, return the relative path but strip the trailing + * slash to mimic Anakia's behavior. + */ + return StringUtils.chop( sb.toString() ); + } + + /** + * Helper method to determine the file separator (forward or + * backward slash) used in a filename. The slash that occurs more + * often is returned as the separator. + * + * @param filename The filename parsed to determine the file + * separator. + * @return The file separator used within filename. + * This value is either a forward or backward slash. + */ + private static String determineSeparator( String filename ) + { + int forwardCount = StringUtils.countMatches( filename, "/" ); + int backwardCount = StringUtils.countMatches( filename, "\\" ); + + return forwardCount >= backwardCount ? "/" : "\\"; + } + + /** + * Cygwin prefers lowercase drive letters, but other parts of maven use uppercase + * + * @param path old path + * @return String + */ +/** + * Cygwin prefers lowercase drive letters, but other parts of maven use uppercase + * + * @param path + * old path + * @return String + */ +static java.lang.String uppercaseDrive(@javax.annotation.Nullable +java.lang.String path) { + { + if ((/* NPEX_NULL_EXP */ + path.length() >= 2) && (path.charAt(1) == ':')) { + path = java.lang.Character.toUpperCase(path.charAt(0)) + path.substring(1); + } + return path; + } +} + + @Nonnull private static String buildRelativePath( @Nonnull String toPath, @Nonnull String fromPath, + final char separatorChar ) + { + // use tokeniser to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialise the tokenisers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatevers left of newPath. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } +} diff --git a/Java/maven-shared-utils-PathTool_270/metadata.json b/Java/maven-shared-utils-PathTool_270/metadata.json new file mode 100644 index 000000000..4343234f4 --- /dev/null +++ b/Java/maven-shared-utils-PathTool_270/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-PathTool_270", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/PathTool.java", + "line": 279, + "npe_method": "uppercaseDrive", + "deref_field": "path", + "npe_class": "PathTool", + "repo": "maven-shared-utils", + "bug_id": "PathTool_270" + } +} diff --git a/Java/maven-shared-utils-PathTool_270/npe.json b/Java/maven-shared-utils-PathTool_270/npe.json new file mode 100644 index 000000000..656dd3585 --- /dev/null +++ b/Java/maven-shared-utils-PathTool_270/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/PathTool.java", + "line": 279, + "npe_method": "uppercaseDrive", + "deref_field": "path", + "npe_class": "PathTool" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-PrettyPrintXMLWriter_365/Dockerfile b/Java/maven-shared-utils-PrettyPrintXMLWriter_365/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-PrettyPrintXMLWriter_365/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-PrettyPrintXMLWriter_365/buggy.java b/Java/maven-shared-utils-PrettyPrintXMLWriter_365/buggy.java new file mode 100644 index 000000000..537ce4087 --- /dev/null +++ b/Java/maven-shared-utils-PrettyPrintXMLWriter_365/buggy.java @@ -0,0 +1,387 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.ArrayList; +import org.apache.maven.shared.utils.Os; + +/** + * XMLWriter with nice indentation. + * + * @author kama + */ +public class PrettyPrintXMLWriter + implements XMLWriter +{ + private static final char[] CLOSE_1 = "/>".toCharArray(); + + private static final char[] CLOSE_2 = " elementStack = new ArrayList(); + + private boolean processingElement = false; + + private boolean documentStarted = false; + + private boolean endOnSameLine = false; + + private int depth = 0; + + private char[] lineIndent; + + private char[] lineSeparator; + + private String encoding; + + private String docType; + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + */ + public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent ) + { + this( writer, lineIndent, null, null ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + */ + public PrettyPrintXMLWriter( Writer writer, String lineIndent ) + { + this( new PrintWriter( writer ), lineIndent ); + } + + /** + * @param writer not null + */ + public PrettyPrintXMLWriter( PrintWriter writer ) + { + this( writer, null, null ); + } + + /** + * @param writer not null + */ + public PrettyPrintXMLWriter( Writer writer ) + { + this( new PrintWriter( writer ) ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param encoding could be null or invalid. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent, String encoding, String doctype ) + { + this( writer, lineIndent.toCharArray(), Os.LINE_SEP.toCharArray(), encoding, doctype ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param encoding could be null or invalid. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( Writer writer, String lineIndent, String encoding, String doctype ) + { + this( new PrintWriter( writer ), lineIndent, encoding, doctype ); + } + + /** + * @param writer not null + * @param encoding could be null or invalid. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( PrintWriter writer, String encoding, String doctype ) + { + this( writer, DEFAULT_LINE_INDENT, Os.LINE_SEP.toCharArray(), encoding, doctype ); + } + + /** + * @param writer not null + * @param encoding could be null or invalid. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( Writer writer, String encoding, String doctype ) + { + this( new PrintWriter( writer ), encoding, doctype ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param lineSeparator could be null, but the normal way is valid line separator + * @param encoding could be null or the encoding to use. + * @param doctype could be null. + */ + public PrettyPrintXMLWriter( PrintWriter writer, String lineIndent, String lineSeparator, String encoding, + String doctype ) + { + this( writer, lineIndent.toCharArray(), lineSeparator.toCharArray(), encoding, doctype ); + } + + /** + * @param writer not null + * @param lineIndent could be null, but the normal way is some spaces. + * @param lineSeparator could be null, but the normal way is valid line separator + * @param encoding could be null or the encoding to use. + * @param doctype could be null. + */ + private PrettyPrintXMLWriter( PrintWriter writer, char[] lineIndent, char[] lineSeparator, String encoding, + String doctype ) + { + super(); + this.writer = writer; + this.lineIndent = lineIndent; + this.lineSeparator = lineSeparator; + this.encoding = encoding; + this.docType = doctype; + + depth = 0; + + // Fail early with assertions enabled. Issue is in the calling code not having checked for any errors. + assert !writer.checkError() : "Unexpected error state PrintWriter passed to PrettyPrintXMLWriter."; + } + + /** {@inheritDoc} */ + public void addAttribute( String key, String value ) throws IOException + { + if ( !processingElement ) + { + throw new IllegalStateException( "currently processing no element" ); + } + + writer.write( ' ' ); + writer.write( key ); + writer.write( '=' ); + XMLEncode.xmlEncodeTextAsPCDATA( value, true, '"', writer ); + if ( writer.checkError() ) + { + throw new IOException( "Failure adding attribute '" + key + "' with value '" + value + "'" ); + } + } + + /** {@inheritDoc} */ + public void setEncoding( String encoding ) + { + if ( documentStarted ) + { + throw new IllegalStateException( "Document headers already written!" ); + } + + this.encoding = encoding; + } + + /** {@inheritDoc} */ + public void setDocType( String docType ) + { + if ( documentStarted ) + { + throw new IllegalStateException( "Document headers already written!" ); + } + + this.docType = docType; + } + + /** + * @param lineSeparator The line separator to be used. + */ + public void setLineSeparator( String lineSeparator ) + { + if ( documentStarted ) + { + throw new IllegalStateException( "Document headers already written!" ); + } + + this.lineSeparator = lineSeparator.toCharArray(); + } + + /** + * @param lineIndentParameter The line indent parameter. + */ + public void setLineIndenter( String lineIndentParameter ) + { + if ( documentStarted ) + { + throw new IllegalStateException( "Document headers already written!" ); + } + + this.lineIndent = lineIndentParameter.toCharArray(); + } + + /** {@inheritDoc} */ + public void startElement( String elementName ) throws IOException + { + boolean firstLine = ensureDocumentStarted(); + + completePreviouslyOpenedElement(); + + if ( !firstLine ) + { + newLine(); + } + + writer.write( '<' ); + writer.write( elementName ); + if ( writer.checkError() ) + { + throw new IOException( "Failure starting element '" + elementName + "'." ); + } + + processingElement = true; + + elementStack.add( depth++, elementName ); + } + + /** {@inheritDoc} */ + public void writeText( String text ) throws IOException + { + ensureDocumentStarted(); + + completePreviouslyOpenedElement(); + + XMLEncode.xmlEncodeText( text, writer ); + + endOnSameLine = true; + + if ( writer.checkError() ) + { + throw new IOException( "Failure writing text." ); + } + } + + /** {@inheritDoc} */ + public void writeMarkup( String markup ) throws IOException + { + ensureDocumentStarted(); + + completePreviouslyOpenedElement(); + + writer.write( markup ); + + if ( writer.checkError() ) + { + throw new IOException( "Failure writing markup." ); + } + } + + /** {@inheritDoc} */ + public void endElement() throws IOException + { + String chars = elementStack.get( --depth ); + if ( processingElement ) + { + // this means we don't have any content yet so we just add a /> + writer.write( CLOSE_1 ); + + processingElement = false; + } + else + { + if ( !endOnSameLine ) + { + newLine(); + } + + // otherwise we need a full closing tag for that element + writer.write( CLOSE_2 ); + writer.write( chars ); + writer.write( '>' ); + } + + endOnSameLine = false; + + if ( writer.checkError() ) + { + throw new IOException( "Failure ending element." ); + } + } + + /** + * Write the documents if not already done. + * + * @return true if the document headers have freshly been written. + */ + private boolean ensureDocumentStarted() + { + if ( !documentStarted ) + { + if ( docType != null || encoding != null ) + { + writeDocumentHeader(); + } + + documentStarted = true; + + return true; + } + + return false; + } + +private void writeDocumentHeader() { + writer.write(""); + newLine(); + { + writer.write("'); + newLine(); + } +} + + private void newLine() + { + writer.write( lineSeparator ); + + for ( int i = 0; i < depth; i++ ) + { + writer.write( lineIndent ); + } + } + + private void completePreviouslyOpenedElement() + { + if ( processingElement ) + { + writer.write( '>' ); + processingElement = false; + } + } + +} diff --git a/Java/maven-shared-utils-PrettyPrintXMLWriter_365/metadata.json b/Java/maven-shared-utils-PrettyPrintXMLWriter_365/metadata.json new file mode 100644 index 000000000..c5c1da92f --- /dev/null +++ b/Java/maven-shared-utils-PrettyPrintXMLWriter_365/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-PrettyPrintXMLWriter_365", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java", + "line": 362, + "npe_method": "writeDocumentHeader", + "deref_field": "docType", + "npe_class": "PrettyPrintXMLWriter", + "repo": "maven-shared-utils", + "bug_id": "PrettyPrintXMLWriter_365" + } +} diff --git a/Java/maven-shared-utils-PrettyPrintXMLWriter_365/npe.json b/Java/maven-shared-utils-PrettyPrintXMLWriter_365/npe.json new file mode 100644 index 000000000..e89d372fe --- /dev/null +++ b/Java/maven-shared-utils-PrettyPrintXMLWriter_365/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/PrettyPrintXMLWriter.java", + "line": 362, + "npe_method": "writeDocumentHeader", + "deref_field": "docType", + "npe_class": "PrettyPrintXMLWriter" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-PropertyUtils_106/Dockerfile b/Java/maven-shared-utils-PropertyUtils_106/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_106/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-PropertyUtils_106/buggy.java b/Java/maven-shared-utils-PropertyUtils_106/buggy.java new file mode 100644 index 000000000..8ded4bbab --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_106/buggy.java @@ -0,0 +1,223 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Static utility methods for loading properties. + */ +public class PropertyUtils +{ + + /** + * The constructor. + * + * @deprecated This is a utility class with only static methods. Don't create instances of it. + */ + @Deprecated + public PropertyUtils() + { + } + + /** + * @param url the URL which should be used to load the properties + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.net.URL)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code URL} is {@code null}. + */ + @Deprecated + public static java.util.Properties loadProperties( @Nonnull URL url ) + { + try ( InputStream in = url.openStream() ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * @param file the file from which the properties will be loaded + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.File)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code File} is {@code null}. + */ + @Deprecated + public static Properties loadProperties( @Nonnull File file ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param is {@link InputStream} + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.InputStream)} instead. This method + * should not be used as it suppresses exceptions silently when loading properties fails. + */ + @Deprecated +/** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param is + * {@link InputStream} + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.InputStream)} instead. This method +should not be used as it suppresses exceptions silently when loading properties fails. + */ +public static java.util.Properties loadProperties(@javax.annotation.Nullable +java.io.InputStream is) { + try { + java.util.Properties result = new java.util.Properties(); + { + try (final java.io.InputStream in = /* NPEX_NULL_EXP */ + is) { + result.load(in); + } catch (java.io.IOException e) { + // ignore + } + } + return result; + } catch (java.lang.Exception e) { + // ignore + } + return null; +} + + /** + * Loads {@code Properties} from a given {@code URL}. + *

          + * If the given {@code URL} is {@code null} or the properties can't be read, an empty properties object is returned. + *

          + * + * @param url the {@code URL} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable URL url ) + { + + Properties properties = new Properties(); + if ( url != null ) + { + try ( InputStream in = url.openStream() ) + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + return properties; + } + + /** + * Loads {@code Properties} from a {@code File}. + *

          + * If the given {@code File} is {@code null} or the properties file can't be read, an empty properties object is + * returned. + *

          + * + * @param file the {@code File} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable File file ) + { + Properties properties = new Properties(); + if ( file != null ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + + return properties; + + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * If the given {@code InputStream} is {@code null} or the properties can't be read, an empty properties object is + * returned. In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param inputStream the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable InputStream inputStream ) + { + + Properties properties = new Properties(); + + if ( inputStream != null ) + { + try ( InputStream in = inputStream ) // reassign inputStream to autoclose + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + + return properties; + + } + +} diff --git a/Java/maven-shared-utils-PropertyUtils_106/metadata.json b/Java/maven-shared-utils-PropertyUtils_106/metadata.json new file mode 100644 index 000000000..4eff8ff9a --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_106/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-PropertyUtils_106", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/PropertyUtils.java", + "line": 118, + "npe_method": "loadProperties", + "deref_field": "is", + "npe_class": "PropertyUtils", + "repo": "maven-shared-utils", + "bug_id": "PropertyUtils_106" + } +} diff --git a/Java/maven-shared-utils-PropertyUtils_106/npe.json b/Java/maven-shared-utils-PropertyUtils_106/npe.json new file mode 100644 index 000000000..25b278a64 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_106/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/PropertyUtils.java", + "line": 118, + "npe_method": "loadProperties", + "deref_field": "is", + "npe_class": "PropertyUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-PropertyUtils_141/Dockerfile b/Java/maven-shared-utils-PropertyUtils_141/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_141/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-PropertyUtils_141/buggy.java b/Java/maven-shared-utils-PropertyUtils_141/buggy.java new file mode 100644 index 000000000..2fb50842a --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_141/buggy.java @@ -0,0 +1,225 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Static utility methods for loading properties. + */ +public class PropertyUtils +{ + + /** + * The constructor. + * + * @deprecated This is a utility class with only static methods. Don't create instances of it. + */ + @Deprecated + public PropertyUtils() + { + } + + /** + * @param url the URL which should be used to load the properties + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.net.URL)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code URL} is {@code null}. + */ + @Deprecated + public static java.util.Properties loadProperties( @Nonnull URL url ) + { + try ( InputStream in = url.openStream() ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * @param file the file from which the properties will be loaded + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.File)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code File} is {@code null}. + */ + @Deprecated + public static Properties loadProperties( @Nonnull File file ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param is {@link InputStream} + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.InputStream)} instead. This method + * should not be used as it suppresses exceptions silently when loading properties fails. + */ + @Deprecated + public static Properties loadProperties( @Nullable InputStream is ) + { + try + { + Properties result = new Properties(); + if ( is != null ) + { + try ( InputStream in = is ) + { + result.load( in ); + } + catch ( IOException e ) + { + // ignore + } + } + return result; + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from a given {@code URL}. + *

          + * If the given {@code URL} is {@code null} or the properties can't be read, an empty properties object is returned. + *

          + * + * @param url the {@code URL} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull +/** + * Loads {@code Properties} from a given {@code URL}. + *

          + * If the given {@code URL} is {@code null} or the properties can't be read, an empty properties object is returned. + *

          + * + * @param url + * the {@code URL} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ +public static java.util.Properties loadOptionalProperties(@javax.annotation.Nullable +final java.net.URL url) { + java.util.Properties properties = new java.util.Properties(); + { + try (final java.io.InputStream in = /* NPEX_NULL_EXP */ + url.openStream()) { + properties.load(in); + } catch (java.lang.IllegalArgumentException | java.io.IOException ex) { + // ignore and return empty properties + } + } + return properties; +} + + /** + * Loads {@code Properties} from a {@code File}. + *

          + * If the given {@code File} is {@code null} or the properties file can't be read, an empty properties object is + * returned. + *

          + * + * @param file the {@code File} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable File file ) + { + Properties properties = new Properties(); + if ( file != null ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + + return properties; + + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * If the given {@code InputStream} is {@code null} or the properties can't be read, an empty properties object is + * returned. In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param inputStream the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable InputStream inputStream ) + { + + Properties properties = new Properties(); + + if ( inputStream != null ) + { + try ( InputStream in = inputStream ) // reassign inputStream to autoclose + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + + return properties; + + } + +} diff --git a/Java/maven-shared-utils-PropertyUtils_141/metadata.json b/Java/maven-shared-utils-PropertyUtils_141/metadata.json new file mode 100644 index 000000000..363c3ea9e --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_141/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-PropertyUtils_141", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/PropertyUtils.java", + "line": 153, + "npe_method": "loadOptionalProperties", + "deref_field": "url", + "npe_class": "PropertyUtils", + "repo": "maven-shared-utils", + "bug_id": "PropertyUtils_141" + } +} diff --git a/Java/maven-shared-utils-PropertyUtils_141/npe.json b/Java/maven-shared-utils-PropertyUtils_141/npe.json new file mode 100644 index 000000000..21262c087 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_141/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/PropertyUtils.java", + "line": 153, + "npe_method": "loadOptionalProperties", + "deref_field": "url", + "npe_class": "PropertyUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-PropertyUtils_170/Dockerfile b/Java/maven-shared-utils-PropertyUtils_170/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_170/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-PropertyUtils_170/buggy.java b/Java/maven-shared-utils-PropertyUtils_170/buggy.java new file mode 100644 index 000000000..73eb98360 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_170/buggy.java @@ -0,0 +1,225 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Static utility methods for loading properties. + */ +public class PropertyUtils +{ + + /** + * The constructor. + * + * @deprecated This is a utility class with only static methods. Don't create instances of it. + */ + @Deprecated + public PropertyUtils() + { + } + + /** + * @param url the URL which should be used to load the properties + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.net.URL)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code URL} is {@code null}. + */ + @Deprecated + public static java.util.Properties loadProperties( @Nonnull URL url ) + { + try ( InputStream in = url.openStream() ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * @param file the file from which the properties will be loaded + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.File)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code File} is {@code null}. + */ + @Deprecated + public static Properties loadProperties( @Nonnull File file ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param is {@link InputStream} + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.InputStream)} instead. This method + * should not be used as it suppresses exceptions silently when loading properties fails. + */ + @Deprecated + public static Properties loadProperties( @Nullable InputStream is ) + { + try + { + Properties result = new Properties(); + if ( is != null ) + { + try ( InputStream in = is ) + { + result.load( in ); + } + catch ( IOException e ) + { + // ignore + } + } + return result; + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from a given {@code URL}. + *

          + * If the given {@code URL} is {@code null} or the properties can't be read, an empty properties object is returned. + *

          + * + * @param url the {@code URL} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable URL url ) + { + + Properties properties = new Properties(); + if ( url != null ) + { + try ( InputStream in = url.openStream() ) + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + return properties; + } + + /** + * Loads {@code Properties} from a {@code File}. + *

          + * If the given {@code File} is {@code null} or the properties file can't be read, an empty properties object is + * returned. + *

          + * + * @param file the {@code File} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull +/** + * Loads {@code Properties} from a {@code File}. + *

          + * If the given {@code File} is {@code null} or the properties file can't be read, an empty properties object is + * returned. + *

          + * + * @param file + * the {@code File} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ +public static java.util.Properties loadOptionalProperties(@javax.annotation.Nullable +final java.io.File file) { + java.util.Properties properties = new java.util.Properties(); + { + try (final java.io.InputStream in = new java.io.FileInputStream(/* NPEX_NULL_EXP */ + file)) { + properties.load(in); + } catch (java.lang.IllegalArgumentException | java.io.IOException ex) { + // ignore and return empty properties + } + } + return properties; +} + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * If the given {@code InputStream} is {@code null} or the properties can't be read, an empty properties object is + * returned. In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param inputStream the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable InputStream inputStream ) + { + + Properties properties = new Properties(); + + if ( inputStream != null ) + { + try ( InputStream in = inputStream ) // reassign inputStream to autoclose + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + + return properties; + + } + +} diff --git a/Java/maven-shared-utils-PropertyUtils_170/metadata.json b/Java/maven-shared-utils-PropertyUtils_170/metadata.json new file mode 100644 index 000000000..aff162f3f --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_170/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-PropertyUtils_170", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/PropertyUtils.java", + "line": 184, + "npe_method": "loadOptionalProperties", + "deref_field": "file", + "npe_class": "PropertyUtils", + "repo": "maven-shared-utils", + "bug_id": "PropertyUtils_170" + } +} diff --git a/Java/maven-shared-utils-PropertyUtils_170/npe.json b/Java/maven-shared-utils-PropertyUtils_170/npe.json new file mode 100644 index 000000000..76cae8d9b --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_170/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/PropertyUtils.java", + "line": 184, + "npe_method": "loadOptionalProperties", + "deref_field": "file", + "npe_class": "PropertyUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-PropertyUtils_202/Dockerfile b/Java/maven-shared-utils-PropertyUtils_202/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_202/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-PropertyUtils_202/buggy.java b/Java/maven-shared-utils-PropertyUtils_202/buggy.java new file mode 100644 index 000000000..49bdaefc1 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_202/buggy.java @@ -0,0 +1,223 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Static utility methods for loading properties. + */ +public class PropertyUtils +{ + + /** + * The constructor. + * + * @deprecated This is a utility class with only static methods. Don't create instances of it. + */ + @Deprecated + public PropertyUtils() + { + } + + /** + * @param url the URL which should be used to load the properties + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.net.URL)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code URL} is {@code null}. + */ + @Deprecated + public static java.util.Properties loadProperties( @Nonnull URL url ) + { + try ( InputStream in = url.openStream() ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * @param file the file from which the properties will be loaded + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.File)} instead. This method should not + * be used as it suppresses exceptions silently when loading properties fails and returns {@code null} + * instead of an empty {@code Properties} instance when the given {@code File} is {@code null}. + */ + @Deprecated + public static Properties loadProperties( @Nonnull File file ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + return loadProperties( in ); + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param is {@link InputStream} + * @return the loaded properties + * @deprecated use {@link #loadOptionalProperties(java.io.InputStream)} instead. This method + * should not be used as it suppresses exceptions silently when loading properties fails. + */ + @Deprecated + public static Properties loadProperties( @Nullable InputStream is ) + { + try + { + Properties result = new Properties(); + if ( is != null ) + { + try ( InputStream in = is ) + { + result.load( in ); + } + catch ( IOException e ) + { + // ignore + } + } + return result; + } + catch ( Exception e ) + { + // ignore + } + return null; + } + + /** + * Loads {@code Properties} from a given {@code URL}. + *

          + * If the given {@code URL} is {@code null} or the properties can't be read, an empty properties object is returned. + *

          + * + * @param url the {@code URL} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable URL url ) + { + + Properties properties = new Properties(); + if ( url != null ) + { + try ( InputStream in = url.openStream() ) + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + return properties; + } + + /** + * Loads {@code Properties} from a {@code File}. + *

          + * If the given {@code File} is {@code null} or the properties file can't be read, an empty properties object is + * returned. + *

          + * + * @param file the {@code File} of the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull + public static Properties loadOptionalProperties( final @Nullable File file ) + { + Properties properties = new Properties(); + if ( file != null ) + { + try ( InputStream in = new FileInputStream( file ) ) + { + properties.load( in ); + } + catch ( IllegalArgumentException | IOException ex ) + { + // ignore and return empty properties + } + } + + return properties; + + } + + /** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * If the given {@code InputStream} is {@code null} or the properties can't be read, an empty properties object is + * returned. In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param inputStream the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ + @Nonnull +/** + * Loads {@code Properties} from an {@code InputStream} and closes the stream. + * If the given {@code InputStream} is {@code null} or the properties can't be read, an empty properties object is + * returned. In a future release, this will no longer close the stream, so callers + * should close the stream themselves. + * + * @param inputStream + * the properties resource to load or {@code null} + * @return the loaded properties or an empty {@code Properties} instance if properties fail to load + * @since 3.1.0 + */ +public static java.util.Properties loadOptionalProperties(@javax.annotation.Nullable +final java.io.InputStream inputStream) { + java.util.Properties properties = new java.util.Properties(); + { + // reassign inputStream to autoclose + try (final java.io.InputStream in = /* NPEX_NULL_EXP */ + inputStream) { + properties.load(in); + } catch (java.lang.IllegalArgumentException | java.io.IOException ex) { + // ignore and return empty properties + } + } + return properties; +} + +} diff --git a/Java/maven-shared-utils-PropertyUtils_202/metadata.json b/Java/maven-shared-utils-PropertyUtils_202/metadata.json new file mode 100644 index 000000000..e93e9d598 --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_202/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-PropertyUtils_202", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/PropertyUtils.java", + "line": 214, + "npe_method": "loadOptionalProperties", + "deref_field": "inputStream", + "npe_class": "PropertyUtils", + "repo": "maven-shared-utils", + "bug_id": "PropertyUtils_202" + } +} diff --git a/Java/maven-shared-utils-PropertyUtils_202/npe.json b/Java/maven-shared-utils-PropertyUtils_202/npe.json new file mode 100644 index 000000000..c97148dac --- /dev/null +++ b/Java/maven-shared-utils-PropertyUtils_202/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/PropertyUtils.java", + "line": 214, + "npe_method": "loadOptionalProperties", + "deref_field": "inputStream", + "npe_class": "PropertyUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Shell_152/Dockerfile b/Java/maven-shared-utils-Shell_152/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Shell_152/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Shell_152/buggy.java b/Java/maven-shared-utils-Shell_152/buggy.java new file mode 100644 index 000000000..4179ebd1e --- /dev/null +++ b/Java/maven-shared-utils-Shell_152/buggy.java @@ -0,0 +1,412 @@ +package org.apache.maven.shared.utils.cli.shell; + +/* + * 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. + */ + + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.maven.shared.utils.StringUtils; + +/** + * Class that abstracts the Shell functionality, + * with subclasses for shells that behave particularly, like

          + * + *

            + *
          • command.com
          • + *
          • cmd.exe
          • + *
          + * + * @author Carlos Sanchez + * + */ +public class Shell + implements Cloneable +{ + private static final char[] DEFAULT_QUOTING_TRIGGER_CHARS = { ' ' }; + + private String shellCommand; + + private final List shellArgs = new ArrayList(); + + private boolean quotedArgumentsEnabled = true; + + private boolean unconditionalQuoting = false; + + private String executable; + + private String workingDir; + + private boolean quotedExecutableEnabled = true; + + private boolean singleQuotedArgumentEscaped = false; + + private boolean singleQuotedExecutableEscaped = false; + + private char argQuoteDelimiter = '\"'; + + private char exeQuoteDelimiter = '\"'; + + /** + * Set the command to execute the shell (e.g. COMMAND.COM, /bin/bash,...). + * + * @param shellCommand the command + */ + void setShellCommand( String shellCommand ) + { + this.shellCommand = shellCommand; + } + + /** + * Get the command to execute the shell. + * + * @return the command + */ + String getShellCommand() + { + return shellCommand; + } + + /** + * Set the shell arguments when calling a command line (not the executable arguments) + * (e.g. /X /C for CMD.EXE). + * + * @param shellArgs the arguments to the shell + */ + void setShellArgs( String[] shellArgs ) + { + this.shellArgs.clear(); + this.shellArgs.addAll( Arrays.asList( shellArgs ) ); + } + + /** + * Get the shell arguments + * + * @return the arguments + */ + String[] getShellArgs() + { + if ( shellArgs.isEmpty() ) + { + return null; + } + else + { + return shellArgs.toArray( new String[0] ); + } + } + + protected String quoteOneItem( String inputString, boolean isExecutable ) + { + char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() ); + return StringUtils.quoteAndEscape( + inputString, + isExecutable ? getExecutableQuoteDelimiter() : getArgumentQuoteDelimiter(), + escapeChars, + getQuotingTriggerChars(), + '\\', + unconditionalQuoting + ); + } + + /** + * Get the command line for the provided executable and arguments in this shell + * + * @param executableParameter executable that the shell has to call + * @param argumentsParameter arguments for the executable, not the shell + * @return list with one String object with executable and arguments quoted as needed + */ + List getCommandLine( String executableParameter, String... argumentsParameter ) + { + return getRawCommandLine( executableParameter, argumentsParameter ); + } + + /** + * @param executableParameter Executable + * @param argumentsParameter the arguments for the executable + * @return the list on command line + */ +/** + * + * + * @param executableParameter + * Executable + * @param argumentsParameter + * the arguments for the executable + * @return the list on command line + */ +java.util.List getRawCommandLine(java.lang.String executableParameter, java.lang.String... argumentsParameter) { + java.util.List commandLine = new java.util.ArrayList<>(); + java.lang.StringBuilder sb = new java.lang.StringBuilder(); + { + java.lang.String preamble = getExecutionPreamble(); + if (preamble != null) { + sb.append(preamble); + } + if (isQuotedExecutableEnabled()) { + sb.append(quoteOneItem(/* NPEX_NULL_EXP */ + executableParameter, true)); + } else { + sb.append(executableParameter); + } + } + for (java.lang.String argument : argumentsParameter) { + if (sb.length() > 0) { + sb.append(' '); + } + if (isQuotedArgumentsEnabled()) { + sb.append(quoteOneItem(argument, false)); + } else { + sb.append(argument); + } + } + commandLine.add(sb.toString()); + return commandLine; +} + + char[] getQuotingTriggerChars() + { + return DEFAULT_QUOTING_TRIGGER_CHARS; + } + + String getExecutionPreamble() + { + return null; + } + + char[] getEscapeChars( boolean includeSingleQuote, boolean includeDoubleQuote ) + { + StringBuilder buf = new StringBuilder( 2 ); + if ( includeSingleQuote ) + { + buf.append( '\'' ); + } + + if ( includeDoubleQuote ) + { + buf.append( '\"' ); + } + + char[] result = new char[buf.length()]; + buf.getChars( 0, buf.length(), result, 0 ); + + return result; + } + + /** + * @return false in all cases + */ + protected boolean isDoubleQuotedArgumentEscaped() + { + return false; + } + + /** + * @return {@link #singleQuotedArgumentEscaped} + */ + protected boolean isSingleQuotedArgumentEscaped() + { + return singleQuotedArgumentEscaped; + } + + boolean isDoubleQuotedExecutableEscaped() + { + return false; + } + + boolean isSingleQuotedExecutableEscaped() + { + return singleQuotedExecutableEscaped; + } + + /** + * @param argQuoteDelimiterParameter {@link #argQuoteDelimiter} + */ + void setArgumentQuoteDelimiter( char argQuoteDelimiterParameter ) + { + this.argQuoteDelimiter = argQuoteDelimiterParameter; + } + + char getArgumentQuoteDelimiter() + { + return argQuoteDelimiter; + } + + /** + * @param exeQuoteDelimiterParameter {@link #exeQuoteDelimiter} + */ + void setExecutableQuoteDelimiter( char exeQuoteDelimiterParameter ) + { + this.exeQuoteDelimiter = exeQuoteDelimiterParameter; + } + + char getExecutableQuoteDelimiter() + { + return exeQuoteDelimiter; + } + + /** + * Get the full command line to execute, including shell command, shell arguments, + * executable and executable arguments + * + * @param arguments arguments for the executable, not the shell + * @return List of String objects, whose array version is suitable to be used as argument + * of Runtime.getRuntime().exec() + */ + public List getShellCommandLine( String... arguments ) + { + + List commandLine = new ArrayList<>(); + + if ( getShellCommand() != null ) + { + commandLine.add( getShellCommand() ); + } + + if ( getShellArgs() != null ) + { + commandLine.addAll( getShellArgsList() ); + } + + commandLine.addAll( getCommandLine( executable, arguments ) ); + + return commandLine; + + } + + List getShellArgsList() + { + return shellArgs; + } + + /** + * @param quotedArgumentsEnabled {@link #quotedArgumentsEnabled} + */ + public void setQuotedArgumentsEnabled( boolean quotedArgumentsEnabled ) + { + this.quotedArgumentsEnabled = quotedArgumentsEnabled; + } + + boolean isQuotedArgumentsEnabled() + { + return quotedArgumentsEnabled; + } + + void setQuotedExecutableEnabled( boolean quotedExecutableEnabled ) + { + this.quotedExecutableEnabled = quotedExecutableEnabled; + } + + boolean isQuotedExecutableEnabled() + { + return quotedExecutableEnabled; + } + + /** + * Sets the executable to run. + * @param executable The executable. + */ + public void setExecutable( String executable ) + { + if ( ( executable == null ) || ( executable.length() == 0 ) ) + { + return; + } + this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + + /** + * @return The executable. + */ + public String getExecutable() + { + return executable; + } + + /** + * Sets execution directory. + * @param path The path which should be used as working directory. + */ + public void setWorkingDirectory( String path ) + { + if ( path != null ) + { + this.workingDir = path; + } + } + + /** + * Sets execution directory. + * + * @param workingDirectory the working directory + */ + public void setWorkingDirectory( File workingDirectory ) + { + if ( workingDirectory != null ) + { + this.workingDir = workingDirectory.getAbsolutePath(); + } + } + + /** + * @return the working directory + */ + public File getWorkingDirectory() + { + return workingDir == null ? null : new File( workingDir ); + } + + String getWorkingDirectoryAsString() + { + return workingDir; + } + + /** {@inheritDoc} */ + public Object clone() + { + throw new RuntimeException( "Do we ever clone this?" ); +/* Shell shell = new Shell(); + shell.setExecutable( getExecutable() ); + shell.setWorkingDirectory( getWorkingDirectory() ); + shell.setShellArgs( getShellArgs() ); + return shell;*/ + } + + void setSingleQuotedArgumentEscaped( boolean singleQuotedArgumentEscaped ) + { + this.singleQuotedArgumentEscaped = singleQuotedArgumentEscaped; + } + + void setSingleQuotedExecutableEscaped( boolean singleQuotedExecutableEscaped ) + { + this.singleQuotedExecutableEscaped = singleQuotedExecutableEscaped; + } + + public boolean isUnconditionalQuoting() + { + return unconditionalQuoting; + } + + public void setUnconditionalQuoting( boolean unconditionalQuoting ) + { + this.unconditionalQuoting = unconditionalQuoting; + } +} diff --git a/Java/maven-shared-utils-Shell_152/metadata.json b/Java/maven-shared-utils-Shell_152/metadata.json new file mode 100644 index 000000000..8b2c07e09 --- /dev/null +++ b/Java/maven-shared-utils-Shell_152/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Shell_152", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java", + "line": 166, + "npe_method": "getRawCommandLine", + "deref_field": "executableParameter", + "npe_class": "Shell", + "repo": "maven-shared-utils", + "bug_id": "Shell_152" + } +} diff --git a/Java/maven-shared-utils-Shell_152/npe.json b/Java/maven-shared-utils-Shell_152/npe.json new file mode 100644 index 000000000..da65f69f6 --- /dev/null +++ b/Java/maven-shared-utils-Shell_152/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java", + "line": 166, + "npe_method": "getRawCommandLine", + "deref_field": "executableParameter", + "npe_class": "Shell" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Shell_155/Dockerfile b/Java/maven-shared-utils-Shell_155/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Shell_155/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Shell_155/buggy.java b/Java/maven-shared-utils-Shell_155/buggy.java new file mode 100644 index 000000000..6a724dc2e --- /dev/null +++ b/Java/maven-shared-utils-Shell_155/buggy.java @@ -0,0 +1,412 @@ +package org.apache.maven.shared.utils.cli.shell; + +/* + * 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. + */ + + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.maven.shared.utils.StringUtils; + +/** + * Class that abstracts the Shell functionality, + * with subclasses for shells that behave particularly, like

          + * + *

            + *
          • command.com
          • + *
          • cmd.exe
          • + *
          + * + * @author Carlos Sanchez + * + */ +public class Shell + implements Cloneable +{ + private static final char[] DEFAULT_QUOTING_TRIGGER_CHARS = { ' ' }; + + private String shellCommand; + + private final List shellArgs = new ArrayList(); + + private boolean quotedArgumentsEnabled = true; + + private boolean unconditionalQuoting = false; + + private String executable; + + private String workingDir; + + private boolean quotedExecutableEnabled = true; + + private boolean singleQuotedArgumentEscaped = false; + + private boolean singleQuotedExecutableEscaped = false; + + private char argQuoteDelimiter = '\"'; + + private char exeQuoteDelimiter = '\"'; + + /** + * Set the command to execute the shell (e.g. COMMAND.COM, /bin/bash,...). + * + * @param shellCommand the command + */ + void setShellCommand( String shellCommand ) + { + this.shellCommand = shellCommand; + } + + /** + * Get the command to execute the shell. + * + * @return the command + */ + String getShellCommand() + { + return shellCommand; + } + + /** + * Set the shell arguments when calling a command line (not the executable arguments) + * (e.g. /X /C for CMD.EXE). + * + * @param shellArgs the arguments to the shell + */ + void setShellArgs( String[] shellArgs ) + { + this.shellArgs.clear(); + this.shellArgs.addAll( Arrays.asList( shellArgs ) ); + } + + /** + * Get the shell arguments + * + * @return the arguments + */ + String[] getShellArgs() + { + if ( shellArgs.isEmpty() ) + { + return null; + } + else + { + return shellArgs.toArray( new String[0] ); + } + } + + protected String quoteOneItem( String inputString, boolean isExecutable ) + { + char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() ); + return StringUtils.quoteAndEscape( + inputString, + isExecutable ? getExecutableQuoteDelimiter() : getArgumentQuoteDelimiter(), + escapeChars, + getQuotingTriggerChars(), + '\\', + unconditionalQuoting + ); + } + + /** + * Get the command line for the provided executable and arguments in this shell + * + * @param executableParameter executable that the shell has to call + * @param argumentsParameter arguments for the executable, not the shell + * @return list with one String object with executable and arguments quoted as needed + */ + List getCommandLine( String executableParameter, String... argumentsParameter ) + { + return getRawCommandLine( executableParameter, argumentsParameter ); + } + + /** + * @param executableParameter Executable + * @param argumentsParameter the arguments for the executable + * @return the list on command line + */ +/** + * + * + * @param executableParameter + * Executable + * @param argumentsParameter + * the arguments for the executable + * @return the list on command line + */ +java.util.List getRawCommandLine(java.lang.String executableParameter, java.lang.String... argumentsParameter) { + java.util.List commandLine = new java.util.ArrayList<>(); + java.lang.StringBuilder sb = new java.lang.StringBuilder(); + { + java.lang.String preamble = getExecutionPreamble(); + { + sb.append(/* NPEX_NULL_EXP */ + preamble); + } + if (isQuotedExecutableEnabled()) { + sb.append(quoteOneItem(executableParameter, true)); + } else { + sb.append(executableParameter); + } + } + for (java.lang.String argument : argumentsParameter) { + if (sb.length() > 0) { + sb.append(' '); + } + if (isQuotedArgumentsEnabled()) { + sb.append(quoteOneItem(argument, false)); + } else { + sb.append(argument); + } + } + commandLine.add(sb.toString()); + return commandLine; +} + + char[] getQuotingTriggerChars() + { + return DEFAULT_QUOTING_TRIGGER_CHARS; + } + + String getExecutionPreamble() + { + return null; + } + + char[] getEscapeChars( boolean includeSingleQuote, boolean includeDoubleQuote ) + { + StringBuilder buf = new StringBuilder( 2 ); + if ( includeSingleQuote ) + { + buf.append( '\'' ); + } + + if ( includeDoubleQuote ) + { + buf.append( '\"' ); + } + + char[] result = new char[buf.length()]; + buf.getChars( 0, buf.length(), result, 0 ); + + return result; + } + + /** + * @return false in all cases + */ + protected boolean isDoubleQuotedArgumentEscaped() + { + return false; + } + + /** + * @return {@link #singleQuotedArgumentEscaped} + */ + protected boolean isSingleQuotedArgumentEscaped() + { + return singleQuotedArgumentEscaped; + } + + boolean isDoubleQuotedExecutableEscaped() + { + return false; + } + + boolean isSingleQuotedExecutableEscaped() + { + return singleQuotedExecutableEscaped; + } + + /** + * @param argQuoteDelimiterParameter {@link #argQuoteDelimiter} + */ + void setArgumentQuoteDelimiter( char argQuoteDelimiterParameter ) + { + this.argQuoteDelimiter = argQuoteDelimiterParameter; + } + + char getArgumentQuoteDelimiter() + { + return argQuoteDelimiter; + } + + /** + * @param exeQuoteDelimiterParameter {@link #exeQuoteDelimiter} + */ + void setExecutableQuoteDelimiter( char exeQuoteDelimiterParameter ) + { + this.exeQuoteDelimiter = exeQuoteDelimiterParameter; + } + + char getExecutableQuoteDelimiter() + { + return exeQuoteDelimiter; + } + + /** + * Get the full command line to execute, including shell command, shell arguments, + * executable and executable arguments + * + * @param arguments arguments for the executable, not the shell + * @return List of String objects, whose array version is suitable to be used as argument + * of Runtime.getRuntime().exec() + */ + public List getShellCommandLine( String... arguments ) + { + + List commandLine = new ArrayList<>(); + + if ( getShellCommand() != null ) + { + commandLine.add( getShellCommand() ); + } + + if ( getShellArgs() != null ) + { + commandLine.addAll( getShellArgsList() ); + } + + commandLine.addAll( getCommandLine( executable, arguments ) ); + + return commandLine; + + } + + List getShellArgsList() + { + return shellArgs; + } + + /** + * @param quotedArgumentsEnabled {@link #quotedArgumentsEnabled} + */ + public void setQuotedArgumentsEnabled( boolean quotedArgumentsEnabled ) + { + this.quotedArgumentsEnabled = quotedArgumentsEnabled; + } + + boolean isQuotedArgumentsEnabled() + { + return quotedArgumentsEnabled; + } + + void setQuotedExecutableEnabled( boolean quotedExecutableEnabled ) + { + this.quotedExecutableEnabled = quotedExecutableEnabled; + } + + boolean isQuotedExecutableEnabled() + { + return quotedExecutableEnabled; + } + + /** + * Sets the executable to run. + * @param executable The executable. + */ + public void setExecutable( String executable ) + { + if ( ( executable == null ) || ( executable.length() == 0 ) ) + { + return; + } + this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + + /** + * @return The executable. + */ + public String getExecutable() + { + return executable; + } + + /** + * Sets execution directory. + * @param path The path which should be used as working directory. + */ + public void setWorkingDirectory( String path ) + { + if ( path != null ) + { + this.workingDir = path; + } + } + + /** + * Sets execution directory. + * + * @param workingDirectory the working directory + */ + public void setWorkingDirectory( File workingDirectory ) + { + if ( workingDirectory != null ) + { + this.workingDir = workingDirectory.getAbsolutePath(); + } + } + + /** + * @return the working directory + */ + public File getWorkingDirectory() + { + return workingDir == null ? null : new File( workingDir ); + } + + String getWorkingDirectoryAsString() + { + return workingDir; + } + + /** {@inheritDoc} */ + public Object clone() + { + throw new RuntimeException( "Do we ever clone this?" ); +/* Shell shell = new Shell(); + shell.setExecutable( getExecutable() ); + shell.setWorkingDirectory( getWorkingDirectory() ); + shell.setShellArgs( getShellArgs() ); + return shell;*/ + } + + void setSingleQuotedArgumentEscaped( boolean singleQuotedArgumentEscaped ) + { + this.singleQuotedArgumentEscaped = singleQuotedArgumentEscaped; + } + + void setSingleQuotedExecutableEscaped( boolean singleQuotedExecutableEscaped ) + { + this.singleQuotedExecutableEscaped = singleQuotedExecutableEscaped; + } + + public boolean isUnconditionalQuoting() + { + return unconditionalQuoting; + } + + public void setUnconditionalQuoting( boolean unconditionalQuoting ) + { + this.unconditionalQuoting = unconditionalQuoting; + } +} diff --git a/Java/maven-shared-utils-Shell_155/metadata.json b/Java/maven-shared-utils-Shell_155/metadata.json new file mode 100644 index 000000000..00711a731 --- /dev/null +++ b/Java/maven-shared-utils-Shell_155/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Shell_155", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java", + "line": 163, + "npe_method": "getRawCommandLine", + "deref_field": "preamble", + "npe_class": "Shell", + "repo": "maven-shared-utils", + "bug_id": "Shell_155" + } +} diff --git a/Java/maven-shared-utils-Shell_155/npe.json b/Java/maven-shared-utils-Shell_155/npe.json new file mode 100644 index 000000000..b7244bf9e --- /dev/null +++ b/Java/maven-shared-utils-Shell_155/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java", + "line": 163, + "npe_method": "getRawCommandLine", + "deref_field": "preamble", + "npe_class": "Shell" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Shell_380/Dockerfile b/Java/maven-shared-utils-Shell_380/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Shell_380/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Shell_380/buggy.java b/Java/maven-shared-utils-Shell_380/buggy.java new file mode 100644 index 000000000..fef7b15f7 --- /dev/null +++ b/Java/maven-shared-utils-Shell_380/buggy.java @@ -0,0 +1,423 @@ +package org.apache.maven.shared.utils.cli.shell; + +/* + * 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. + */ + + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.maven.shared.utils.StringUtils; + +/** + * Class that abstracts the Shell functionality, + * with subclasses for shells that behave particularly, like

          + * + *

            + *
          • command.com
          • + *
          • cmd.exe
          • + *
          + * + * @author Carlos Sanchez + * + */ +public class Shell + implements Cloneable +{ + private static final char[] DEFAULT_QUOTING_TRIGGER_CHARS = { ' ' }; + + private String shellCommand; + + private final List shellArgs = new ArrayList(); + + private boolean quotedArgumentsEnabled = true; + + private boolean unconditionalQuoting = false; + + private String executable; + + private String workingDir; + + private boolean quotedExecutableEnabled = true; + + private boolean singleQuotedArgumentEscaped = false; + + private boolean singleQuotedExecutableEscaped = false; + + private char argQuoteDelimiter = '\"'; + + private char exeQuoteDelimiter = '\"'; + + /** + * Set the command to execute the shell (e.g. COMMAND.COM, /bin/bash,...). + * + * @param shellCommand the command + */ + void setShellCommand( String shellCommand ) + { + this.shellCommand = shellCommand; + } + + /** + * Get the command to execute the shell. + * + * @return the command + */ + String getShellCommand() + { + return shellCommand; + } + + /** + * Set the shell arguments when calling a command line (not the executable arguments) + * (e.g. /X /C for CMD.EXE). + * + * @param shellArgs the arguments to the shell + */ + void setShellArgs( String[] shellArgs ) + { + this.shellArgs.clear(); + this.shellArgs.addAll( Arrays.asList( shellArgs ) ); + } + + /** + * Get the shell arguments + * + * @return the arguments + */ + String[] getShellArgs() + { + if ( shellArgs.isEmpty() ) + { + return null; + } + else + { + return shellArgs.toArray( new String[0] ); + } + } + + protected String quoteOneItem( String inputString, boolean isExecutable ) + { + char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() ); + return StringUtils.quoteAndEscape( + inputString, + isExecutable ? getExecutableQuoteDelimiter() : getArgumentQuoteDelimiter(), + escapeChars, + getQuotingTriggerChars(), + '\\', + unconditionalQuoting + ); + } + + /** + * Get the command line for the provided executable and arguments in this shell + * + * @param executableParameter executable that the shell has to call + * @param argumentsParameter arguments for the executable, not the shell + * @return list with one String object with executable and arguments quoted as needed + */ + List getCommandLine( String executableParameter, String... argumentsParameter ) + { + return getRawCommandLine( executableParameter, argumentsParameter ); + } + + /** + * @param executableParameter Executable + * @param argumentsParameter the arguments for the executable + * @return the list on command line + */ + List getRawCommandLine( String executableParameter, String... argumentsParameter ) + { + List commandLine = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + + if ( executableParameter != null ) + { + String preamble = getExecutionPreamble(); + if ( preamble != null ) + { + sb.append( preamble ); + } + + if ( isQuotedExecutableEnabled() ) + { + sb.append( quoteOneItem( executableParameter, true ) ); + } + else + { + sb.append( executableParameter ); + } + } + for ( String argument : argumentsParameter ) + { + if ( sb.length() > 0 ) + { + sb.append( ' ' ); + } + + if ( isQuotedArgumentsEnabled() ) + { + sb.append( quoteOneItem( argument, false ) ); + } + else + { + sb.append( argument ); + } + } + + commandLine.add( sb.toString() ); + + return commandLine; + } + + char[] getQuotingTriggerChars() + { + return DEFAULT_QUOTING_TRIGGER_CHARS; + } + + String getExecutionPreamble() + { + return null; + } + + char[] getEscapeChars( boolean includeSingleQuote, boolean includeDoubleQuote ) + { + StringBuilder buf = new StringBuilder( 2 ); + if ( includeSingleQuote ) + { + buf.append( '\'' ); + } + + if ( includeDoubleQuote ) + { + buf.append( '\"' ); + } + + char[] result = new char[buf.length()]; + buf.getChars( 0, buf.length(), result, 0 ); + + return result; + } + + /** + * @return false in all cases + */ + protected boolean isDoubleQuotedArgumentEscaped() + { + return false; + } + + /** + * @return {@link #singleQuotedArgumentEscaped} + */ + protected boolean isSingleQuotedArgumentEscaped() + { + return singleQuotedArgumentEscaped; + } + + boolean isDoubleQuotedExecutableEscaped() + { + return false; + } + + boolean isSingleQuotedExecutableEscaped() + { + return singleQuotedExecutableEscaped; + } + + /** + * @param argQuoteDelimiterParameter {@link #argQuoteDelimiter} + */ + void setArgumentQuoteDelimiter( char argQuoteDelimiterParameter ) + { + this.argQuoteDelimiter = argQuoteDelimiterParameter; + } + + char getArgumentQuoteDelimiter() + { + return argQuoteDelimiter; + } + + /** + * @param exeQuoteDelimiterParameter {@link #exeQuoteDelimiter} + */ + void setExecutableQuoteDelimiter( char exeQuoteDelimiterParameter ) + { + this.exeQuoteDelimiter = exeQuoteDelimiterParameter; + } + + char getExecutableQuoteDelimiter() + { + return exeQuoteDelimiter; + } + + /** + * Get the full command line to execute, including shell command, shell arguments, + * executable and executable arguments + * + * @param arguments arguments for the executable, not the shell + * @return List of String objects, whose array version is suitable to be used as argument + * of Runtime.getRuntime().exec() + */ + public List getShellCommandLine( String... arguments ) + { + + List commandLine = new ArrayList<>(); + + if ( getShellCommand() != null ) + { + commandLine.add( getShellCommand() ); + } + + if ( getShellArgs() != null ) + { + commandLine.addAll( getShellArgsList() ); + } + + commandLine.addAll( getCommandLine( executable, arguments ) ); + + return commandLine; + + } + + List getShellArgsList() + { + return shellArgs; + } + + /** + * @param quotedArgumentsEnabled {@link #quotedArgumentsEnabled} + */ + public void setQuotedArgumentsEnabled( boolean quotedArgumentsEnabled ) + { + this.quotedArgumentsEnabled = quotedArgumentsEnabled; + } + + boolean isQuotedArgumentsEnabled() + { + return quotedArgumentsEnabled; + } + + void setQuotedExecutableEnabled( boolean quotedExecutableEnabled ) + { + this.quotedExecutableEnabled = quotedExecutableEnabled; + } + + boolean isQuotedExecutableEnabled() + { + return quotedExecutableEnabled; + } + + /** + * Sets the executable to run. + * @param executable The executable. + */ + public void setExecutable( String executable ) + { + if ( ( executable == null ) || ( executable.length() == 0 ) ) + { + return; + } + this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + + /** + * @return The executable. + */ + public String getExecutable() + { + return executable; + } + + /** + * Sets execution directory. + * @param path The path which should be used as working directory. + */ + public void setWorkingDirectory( String path ) + { + if ( path != null ) + { + this.workingDir = path; + } + } + + /** + * Sets execution directory. + * + * @param workingDirectory the working directory + */ + public void setWorkingDirectory( File workingDirectory ) + { + if ( workingDirectory != null ) + { + this.workingDir = workingDirectory.getAbsolutePath(); + } + } + + /** + * @return the working directory + */ +/** + * + * + * @return the working directory + */ +public java.io.File getWorkingDirectory() { + return new java.io.File(/* NPEX_NULL_EXP */ + workingDir); +} + + String getWorkingDirectoryAsString() + { + return workingDir; + } + + /** {@inheritDoc} */ + public Object clone() + { + throw new RuntimeException( "Do we ever clone this?" ); +/* Shell shell = new Shell(); + shell.setExecutable( getExecutable() ); + shell.setWorkingDirectory( getWorkingDirectory() ); + shell.setShellArgs( getShellArgs() ); + return shell;*/ + } + + void setSingleQuotedArgumentEscaped( boolean singleQuotedArgumentEscaped ) + { + this.singleQuotedArgumentEscaped = singleQuotedArgumentEscaped; + } + + void setSingleQuotedExecutableEscaped( boolean singleQuotedExecutableEscaped ) + { + this.singleQuotedExecutableEscaped = singleQuotedExecutableEscaped; + } + + public boolean isUnconditionalQuoting() + { + return unconditionalQuoting; + } + + public void setUnconditionalQuoting( boolean unconditionalQuoting ) + { + this.unconditionalQuoting = unconditionalQuoting; + } +} diff --git a/Java/maven-shared-utils-Shell_380/metadata.json b/Java/maven-shared-utils-Shell_380/metadata.json new file mode 100644 index 000000000..7b3d14a72 --- /dev/null +++ b/Java/maven-shared-utils-Shell_380/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Shell_380", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java", + "line": 385, + "npe_method": "getWorkingDirectory", + "deref_field": "workingDir", + "npe_class": "Shell", + "repo": "maven-shared-utils", + "bug_id": "Shell_380" + } +} diff --git a/Java/maven-shared-utils-Shell_380/npe.json b/Java/maven-shared-utils-Shell_380/npe.json new file mode 100644 index 000000000..18772ae52 --- /dev/null +++ b/Java/maven-shared-utils-Shell_380/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java", + "line": 385, + "npe_method": "getWorkingDirectory", + "deref_field": "workingDir", + "npe_class": "Shell" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1340/Dockerfile b/Java/maven-shared-utils-StringUtils_1340/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1340/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1340/buggy.java b/Java/maven-shared-utils-StringUtils_1340/buggy.java new file mode 100644 index 000000000..3f72090ab --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1340/buggy.java @@ -0,0 +1,2556 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ +/** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str + * the String to remove characters from + * @param strip + * the String to remove + * @return the stripped String + */ +public static java.lang.String stripEnd(java.lang.String str, @javax.annotation.Nullable +java.lang.String strip) { + { + int end = /* NPEX_NULL_EXP */ + str.length(); + if (strip == null) { + while ((end != 0) && java.lang.Character.isWhitespace(str.charAt(end - 1))) { + end--; + } + } else { + while ((end != 0) && (strip.indexOf(str.charAt(end - 1)) != (-1))) { + end--; + } + } + return str.substring(0, end); + } +} + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1340/metadata.json b/Java/maven-shared-utils-StringUtils_1340/metadata.json new file mode 100644 index 000000000..85bac4125 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1340/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1340", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1354, + "npe_method": "stripEnd", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1340" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1340/npe.json b/Java/maven-shared-utils-StringUtils_1340/npe.json new file mode 100644 index 000000000..c75d3a027 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1340/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1354, + "npe_method": "stripEnd", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1346/Dockerfile b/Java/maven-shared-utils-StringUtils_1346/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1346/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1346/buggy.java b/Java/maven-shared-utils-StringUtils_1346/buggy.java new file mode 100644 index 000000000..74554b43f --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1346/buggy.java @@ -0,0 +1,2553 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ +/** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str + * the String to remove characters from + * @param strip + * the String to remove + * @return the stripped String + */ +public static java.lang.String stripEnd(java.lang.String str, @javax.annotation.Nullable +java.lang.String strip) { + if (str == null) { + return null; + } + int end = str.length(); + { + while ((end != 0) && (/* NPEX_NULL_EXP */ + strip.indexOf(str.charAt(end - 1)) != (-1))) { + end--; + } + } + return str.substring(0, end); +} + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1346/metadata.json b/Java/maven-shared-utils-StringUtils_1346/metadata.json new file mode 100644 index 000000000..5cf2cc8f1 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1346/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1346", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1358, + "npe_method": "stripEnd", + "deref_field": "strip", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1346" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1346/npe.json b/Java/maven-shared-utils-StringUtils_1346/npe.json new file mode 100644 index 000000000..12daea34f --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1346/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1358, + "npe_method": "stripEnd", + "deref_field": "strip", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1375/Dockerfile b/Java/maven-shared-utils-StringUtils_1375/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1375/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1375/buggy.java b/Java/maven-shared-utils-StringUtils_1375/buggy.java new file mode 100644 index 000000000..8fe57b26f --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1375/buggy.java @@ -0,0 +1,2554 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ +/** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str + * the String to remove characters from + * @param strip + * the String to remove + * @return the stripped String + */ +public static java.lang.String stripStart(java.lang.String str, @javax.annotation.Nullable +java.lang.String strip) { + { + int start = 0; + int sz = /* NPEX_NULL_EXP */ + str.length(); + if (strip == null) { + while ((start != sz) && java.lang.Character.isWhitespace(str.charAt(start))) { + start++; + } + } else { + while ((start != sz) && (strip.indexOf(str.charAt(start)) != (-1))) { + start++; + } + } + return str.substring(start); + } +} + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1375/metadata.json b/Java/maven-shared-utils-StringUtils_1375/metadata.json new file mode 100644 index 000000000..abd3d4007 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1375/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1375", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1390, + "npe_method": "stripStart", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1375" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1375/npe.json b/Java/maven-shared-utils-StringUtils_1375/npe.json new file mode 100644 index 000000000..59f392abc --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1375/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1390, + "npe_method": "stripStart", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1384/Dockerfile b/Java/maven-shared-utils-StringUtils_1384/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1384/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1384/buggy.java b/Java/maven-shared-utils-StringUtils_1384/buggy.java new file mode 100644 index 000000000..37280a149 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1384/buggy.java @@ -0,0 +1,2551 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ +/** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str + * the String to remove characters from + * @param strip + * the String to remove + * @return the stripped String + */ +public static java.lang.String stripStart(java.lang.String str, @javax.annotation.Nullable +java.lang.String strip) { + if (str == null) { + return null; + } + int start = 0; + int sz = str.length(); + { + while ((start != sz) && (/* NPEX_NULL_EXP */ + strip.indexOf(str.charAt(start)) != (-1))) { + start++; + } + } + return str.substring(start); +} + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1384/metadata.json b/Java/maven-shared-utils-StringUtils_1384/metadata.json new file mode 100644 index 000000000..8f927981d --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1384/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1384", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1394, + "npe_method": "stripStart", + "deref_field": "strip", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1384" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1384/npe.json b/Java/maven-shared-utils-StringUtils_1384/npe.json new file mode 100644 index 000000000..499f03824 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1384/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1394, + "npe_method": "stripStart", + "deref_field": "strip", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1413/Dockerfile b/Java/maven-shared-utils-StringUtils_1413/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1413/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1413/buggy.java b/Java/maven-shared-utils-StringUtils_1413/buggy.java new file mode 100644 index 000000000..40f9fb403 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1413/buggy.java @@ -0,0 +1,2559 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ +// Case conversion +// -------------------------------------------------------------------------- +/** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str + * the String to uppercase + * @return the upper cased String + */ +public static java.lang.String upperCase(java.lang.String str) { + { + return /* NPEX_NULL_EXP */ + str.toUpperCase(); + } +} + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1413/metadata.json b/Java/maven-shared-utils-StringUtils_1413/metadata.json new file mode 100644 index 000000000..07af6a493 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1413/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1413", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1424, + "npe_method": "upperCase", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1413" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1413/npe.json b/Java/maven-shared-utils-StringUtils_1413/npe.json new file mode 100644 index 000000000..000d586fb --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1413/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1424, + "npe_method": "upperCase", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1429/Dockerfile b/Java/maven-shared-utils-StringUtils_1429/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1429/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1429/buggy.java b/Java/maven-shared-utils-StringUtils_1429/buggy.java new file mode 100644 index 000000000..bbbd4fcc2 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1429/buggy.java @@ -0,0 +1,2557 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ +/** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str + * the string to lowercase + * @return the lower cased String + */ +public static java.lang.String lowerCase(java.lang.String str) { + { + return /* NPEX_NULL_EXP */ + str.toLowerCase(); + } +} + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1429/metadata.json b/Java/maven-shared-utils-StringUtils_1429/metadata.json new file mode 100644 index 000000000..6d017d5b2 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1429/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1429", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1438, + "npe_method": "lowerCase", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1429" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1429/npe.json b/Java/maven-shared-utils-StringUtils_1429/npe.json new file mode 100644 index 000000000..db6681eb1 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1429/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1438, + "npe_method": "lowerCase", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1447/Dockerfile b/Java/maven-shared-utils-StringUtils_1447/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1447/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1447/buggy.java b/Java/maven-shared-utils-StringUtils_1447/buggy.java new file mode 100644 index 000000000..050ccaa0f --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1447/buggy.java @@ -0,0 +1,2550 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ +/** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str + * the String to uncapitalise + * @return uncapitalised String + */ +public static java.lang.String uncapitalise(java.lang.String str) { + { + int length = /* NPEX_NULL_EXP */ + str.length(); + if (length == 0) { + return ""; + } else { + return new java.lang.StringBuffer(length).append(java.lang.Character.toLowerCase(str.charAt(0))).append(str, 1, length).toString(); + } + } +} + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1447/metadata.json b/Java/maven-shared-utils-StringUtils_1447/metadata.json new file mode 100644 index 000000000..f7542283e --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1447/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1447", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1458, + "npe_method": "uncapitalise", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1447" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1447/npe.json b/Java/maven-shared-utils-StringUtils_1447/npe.json new file mode 100644 index 000000000..1fe7ba77a --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1447/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1458, + "npe_method": "uncapitalise", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1479/Dockerfile b/Java/maven-shared-utils-StringUtils_1479/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1479/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1479/buggy.java b/Java/maven-shared-utils-StringUtils_1479/buggy.java new file mode 100644 index 000000000..f618402b5 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1479/buggy.java @@ -0,0 +1,2550 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ +/** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str + * the String to capitalise + * @return capitalised String + */ +public static java.lang.String capitalise(java.lang.String str) { + { + int length = /* NPEX_NULL_EXP */ + str.length(); + if (length == 0) { + return ""; + } else { + return new java.lang.StringBuilder(length).append(java.lang.Character.toTitleCase(str.charAt(0))).append(str, 1, length).toString(); + } + } +} + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1479/metadata.json b/Java/maven-shared-utils-StringUtils_1479/metadata.json new file mode 100644 index 000000000..72063a72f --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1479/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1479", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1490, + "npe_method": "capitalise", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1479" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1479/npe.json b/Java/maven-shared-utils-StringUtils_1479/npe.json new file mode 100644 index 000000000..abe388706 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1479/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1490, + "npe_method": "capitalise", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1513/Dockerfile b/Java/maven-shared-utils-StringUtils_1513/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1513/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1513/buggy.java b/Java/maven-shared-utils-StringUtils_1513/buggy.java new file mode 100644 index 000000000..0967cb639 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1513/buggy.java @@ -0,0 +1,2548 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ +/** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str + * the String to swap the case of + * @return the modified String + */ +public static java.lang.String swapCase(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + java.lang.StringBuilder buffer = new java.lang.StringBuilder(sz); + boolean whitespace = false; + char ch; + char tmp; + for (int i = 0; i < sz; i++) { + ch = str.charAt(i); + if (java.lang.Character.isUpperCase(ch)) { + tmp = java.lang.Character.toLowerCase(ch); + } else if (java.lang.Character.isTitleCase(ch)) { + tmp = java.lang.Character.toLowerCase(ch); + } else if (java.lang.Character.isLowerCase(ch)) { + if (whitespace) { + tmp = java.lang.Character.toTitleCase(ch); + } else { + tmp = java.lang.Character.toUpperCase(ch); + } + } else { + tmp = ch; + } + buffer.append(tmp); + whitespace = java.lang.Character.isWhitespace(ch); + } + return buffer.toString(); + } +} + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1513/metadata.json b/Java/maven-shared-utils-StringUtils_1513/metadata.json new file mode 100644 index 000000000..da7545703 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1513/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1513", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1526, + "npe_method": "swapCase", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1513" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1513/npe.json b/Java/maven-shared-utils-StringUtils_1513/npe.json new file mode 100644 index 000000000..a47e2bab1 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1513/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1526, + "npe_method": "swapCase", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1570/Dockerfile b/Java/maven-shared-utils-StringUtils_1570/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1570/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1570/buggy.java b/Java/maven-shared-utils-StringUtils_1570/buggy.java new file mode 100644 index 000000000..6dd6b05b8 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1570/buggy.java @@ -0,0 +1,2555 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ +/** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str + * the String to capitalise + * @return capitalised String + */ +public static java.lang.String capitaliseAllWords(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + java.lang.StringBuilder buffer = new java.lang.StringBuilder(sz); + boolean space = true; + for (int i = 0; i < sz; i++) { + char ch = str.charAt(i); + if (java.lang.Character.isWhitespace(ch)) { + buffer.append(ch); + space = true; + } else if (space) { + buffer.append(java.lang.Character.toTitleCase(ch)); + space = false; + } else { + buffer.append(ch); + } + } + return buffer.toString(); + } +} + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1570/metadata.json b/Java/maven-shared-utils-StringUtils_1570/metadata.json new file mode 100644 index 000000000..b14e29466 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1570/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1570", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1583, + "npe_method": "capitaliseAllWords", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1570" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1570/npe.json b/Java/maven-shared-utils-StringUtils_1570/npe.json new file mode 100644 index 000000000..78cb3d1af --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1570/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1583, + "npe_method": "capitaliseAllWords", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1611/Dockerfile b/Java/maven-shared-utils-StringUtils_1611/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1611/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1611/buggy.java b/Java/maven-shared-utils-StringUtils_1611/buggy.java new file mode 100644 index 000000000..74c2bfec4 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1611/buggy.java @@ -0,0 +1,2555 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ +/** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str + * the string to uncapitalise + * @return uncapitalised string + */ +public static java.lang.String uncapitaliseAllWords(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + java.lang.StringBuilder buffer = new java.lang.StringBuilder(sz); + boolean space = true; + for (int i = 0; i < sz; i++) { + char ch = str.charAt(i); + if (java.lang.Character.isWhitespace(ch)) { + buffer.append(ch); + space = true; + } else if (space) { + buffer.append(java.lang.Character.toLowerCase(ch)); + space = false; + } else { + buffer.append(ch); + } + } + return buffer.toString(); + } +} + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1611/metadata.json b/Java/maven-shared-utils-StringUtils_1611/metadata.json new file mode 100644 index 000000000..4aedb61b0 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1611/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1611", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1624, + "npe_method": "uncapitaliseAllWords", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1611" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1611/npe.json b/Java/maven-shared-utils-StringUtils_1611/npe.json new file mode 100644 index 000000000..6862917b0 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1611/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1624, + "npe_method": "uncapitaliseAllWords", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1670/Dockerfile b/Java/maven-shared-utils-StringUtils_1670/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1670/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1670/buggy.java b/Java/maven-shared-utils-StringUtils_1670/buggy.java new file mode 100644 index 000000000..acb9492a8 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1670/buggy.java @@ -0,0 +1,2562 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ +/** + *

          Get the String that is nested in between two Strings.

          + * + * @param str + * the String containing nested-string + * @param open + * the String before nested-string + * @param close + * the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException + * if open or close is null + */ +public static java.lang.String getNestedString(java.lang.String str, @javax.annotation.Nonnull +java.lang.String open, @javax.annotation.Nonnull +java.lang.String close) { + { + int start = /* NPEX_NULL_EXP */ + str.indexOf(open); + if (start != (-1)) { + int end = str.indexOf(close, start + open.length()); + if (end != (-1)) { + return str.substring(start + open.length(), end); + } + } + return null; + } +} + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1670/metadata.json b/Java/maven-shared-utils-StringUtils_1670/metadata.json new file mode 100644 index 000000000..9d97f3ed3 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1670/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1670", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1686, + "npe_method": "getNestedString", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1670" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1670/npe.json b/Java/maven-shared-utils-StringUtils_1670/npe.json new file mode 100644 index 000000000..04ba0cc08 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1670/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1686, + "npe_method": "getNestedString", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1702/Dockerfile b/Java/maven-shared-utils-StringUtils_1702/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1702/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1702/buggy.java b/Java/maven-shared-utils-StringUtils_1702/buggy.java new file mode 100644 index 000000000..0e58e0c87 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1702/buggy.java @@ -0,0 +1,2562 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ +/** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str + * the String to check + * @param sub + * the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException + * if sub is null + */ +public static int countMatches(@javax.annotation.Nullable +java.lang.String str, @javax.annotation.Nonnull +java.lang.String sub) { + if (sub.equals("")) { + return 0; + } + { + int count = 0; + int idx = 0; + while ((idx = /* NPEX_NULL_EXP */ + str.indexOf(sub, idx)) != (-1)) { + count++; + idx += sub.length(); + } + return count; + } +} + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1702/metadata.json b/Java/maven-shared-utils-StringUtils_1702/metadata.json new file mode 100644 index 000000000..8746cbfd6 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1702/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1702", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1719, + "npe_method": "countMatches", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1702" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1702/npe.json b/Java/maven-shared-utils-StringUtils_1702/npe.json new file mode 100644 index 000000000..f70d0767e --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1702/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1719, + "npe_method": "countMatches", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1730/Dockerfile b/Java/maven-shared-utils-StringUtils_1730/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1730/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1730/buggy.java b/Java/maven-shared-utils-StringUtils_1730/buggy.java new file mode 100644 index 000000000..e2a7556f3 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1730/buggy.java @@ -0,0 +1,2559 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ +// Character Tests +// -------------------------------------------------------------------------- +/** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str + * the String to check + * @return true if only contains letters, and is non-null + */ +public static boolean isAlpha(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + for (int i = 0; i < sz; i++) { + if (!java.lang.Character.isLetter(str.charAt(i))) { + return false; + } + } + return true; + } +} + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1730/metadata.json b/Java/maven-shared-utils-StringUtils_1730/metadata.json new file mode 100644 index 000000000..da366d53d --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1730/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1730", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1743, + "npe_method": "isAlpha", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1730" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1730/npe.json b/Java/maven-shared-utils-StringUtils_1730/npe.json new file mode 100644 index 000000000..9cd861ab0 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1730/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1743, + "npe_method": "isAlpha", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1756/Dockerfile b/Java/maven-shared-utils-StringUtils_1756/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1756/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1756/buggy.java b/Java/maven-shared-utils-StringUtils_1756/buggy.java new file mode 100644 index 000000000..9ced92f78 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1756/buggy.java @@ -0,0 +1,2557 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ +/** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str + * the String to check + * @return true if only contains whitespace, and is non-null + */ +public static boolean isWhitespace(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + for (int i = 0; i < sz; i++) { + if (!java.lang.Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } +} + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1756/metadata.json b/Java/maven-shared-utils-StringUtils_1756/metadata.json new file mode 100644 index 000000000..d4d08cc70 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1756/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1756", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1767, + "npe_method": "isWhitespace", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1756" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1756/npe.json b/Java/maven-shared-utils-StringUtils_1756/npe.json new file mode 100644 index 000000000..c32f708e4 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1756/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1767, + "npe_method": "isWhitespace", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1784/Dockerfile b/Java/maven-shared-utils-StringUtils_1784/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1784/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1784/buggy.java b/Java/maven-shared-utils-StringUtils_1784/buggy.java new file mode 100644 index 000000000..2d8c3eda1 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1784/buggy.java @@ -0,0 +1,2559 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ +/** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str + * the String to check + * @return true if only contains letters and space, +and is non-null + */ +public static boolean isAlphaSpace(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + for (int i = 0; i < sz; i++) { + if ((!java.lang.Character.isLetter(str.charAt(i))) && (str.charAt(i) != ' ')) { + return false; + } + } + return true; + } +} + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1784/metadata.json b/Java/maven-shared-utils-StringUtils_1784/metadata.json new file mode 100644 index 000000000..2c45dafcc --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1784/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1784", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1797, + "npe_method": "isAlphaSpace", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1784" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1784/npe.json b/Java/maven-shared-utils-StringUtils_1784/npe.json new file mode 100644 index 000000000..b86ca4c69 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1784/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1797, + "npe_method": "isAlphaSpace", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1811/Dockerfile b/Java/maven-shared-utils-StringUtils_1811/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1811/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1811/buggy.java b/Java/maven-shared-utils-StringUtils_1811/buggy.java new file mode 100644 index 000000000..442567f12 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1811/buggy.java @@ -0,0 +1,2558 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ +/** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str + * the String to check + * @return true if only contains letters or digits, +and is non-null + */ +public static boolean isAlphanumeric(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + for (int i = 0; i < sz; i++) { + if (!java.lang.Character.isLetterOrDigit(str.charAt(i))) { + return false; + } + } + return true; + } +} + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1811/metadata.json b/Java/maven-shared-utils-StringUtils_1811/metadata.json new file mode 100644 index 000000000..920d3c3a3 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1811/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1811", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1823, + "npe_method": "isAlphanumeric", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1811" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1811/npe.json b/Java/maven-shared-utils-StringUtils_1811/npe.json new file mode 100644 index 000000000..f7f6335fb --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1811/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1823, + "npe_method": "isAlphanumeric", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1839/Dockerfile b/Java/maven-shared-utils-StringUtils_1839/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1839/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1839/buggy.java b/Java/maven-shared-utils-StringUtils_1839/buggy.java new file mode 100644 index 000000000..c0d6e8d8f --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1839/buggy.java @@ -0,0 +1,2559 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ +/** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str + * the String to check + * @return true if only contains letters, digits or space, +and is non-null + */ +public static boolean isAlphanumericSpace(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + for (int i = 0; i < sz; i++) { + if ((!java.lang.Character.isLetterOrDigit(str.charAt(i))) && (str.charAt(i) != ' ')) { + return false; + } + } + return true; + } +} + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1839/metadata.json b/Java/maven-shared-utils-StringUtils_1839/metadata.json new file mode 100644 index 000000000..0a716e572 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1839/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1839", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1852, + "npe_method": "isAlphanumericSpace", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1839" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1839/npe.json b/Java/maven-shared-utils-StringUtils_1839/npe.json new file mode 100644 index 000000000..f8b20fe7c --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1839/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1852, + "npe_method": "isAlphanumericSpace", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1865/Dockerfile b/Java/maven-shared-utils-StringUtils_1865/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1865/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1865/buggy.java b/Java/maven-shared-utils-StringUtils_1865/buggy.java new file mode 100644 index 000000000..b27226536 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1865/buggy.java @@ -0,0 +1,2557 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ +/** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str + * the String to check + * @return true if only contains digits, and is non-null + */ +public static boolean isNumeric(java.lang.String str) { + { + int sz = /* NPEX_NULL_EXP */ + str.length(); + for (int i = 0; i < sz; i++) { + if (!java.lang.Character.isDigit(str.charAt(i))) { + return false; + } + } + return true; + } +} + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1865/metadata.json b/Java/maven-shared-utils-StringUtils_1865/metadata.json new file mode 100644 index 000000000..1550a9d1a --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1865/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1865", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1876, + "npe_method": "isNumeric", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1865" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1865/npe.json b/Java/maven-shared-utils-StringUtils_1865/npe.json new file mode 100644 index 000000000..3d0b7593a --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1865/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1876, + "npe_method": "isNumeric", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1914/Dockerfile b/Java/maven-shared-utils-StringUtils_1914/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1914/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1914/buggy.java b/Java/maven-shared-utils-StringUtils_1914/buggy.java new file mode 100644 index 000000000..152a956b2 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1914/buggy.java @@ -0,0 +1,2566 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated +/** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj + * the Object to check + * @param defaultString + * the default String to return if str is + * null + * @return the passed in string, or the default if it was +null + * @deprecated use {@code java.lang.Objects.toString()} + */ +public static java.lang.String defaultString(java.lang.Object obj, @javax.annotation.Nonnull +java.lang.String defaultString) { + return /* NPEX_NULL_EXP */ + obj.toString(); +} + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1914/metadata.json b/Java/maven-shared-utils-StringUtils_1914/metadata.json new file mode 100644 index 000000000..86b38394d --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1914/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1914", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1929, + "npe_method": "defaultString", + "deref_field": "obj", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1914" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1914/npe.json b/Java/maven-shared-utils-StringUtils_1914/npe.json new file mode 100644 index 000000000..189cbc3c5 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1914/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1929, + "npe_method": "defaultString", + "deref_field": "obj", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_1930/Dockerfile b/Java/maven-shared-utils-StringUtils_1930/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1930/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_1930/buggy.java b/Java/maven-shared-utils-StringUtils_1930/buggy.java new file mode 100644 index 000000000..6ab3d65e7 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1930/buggy.java @@ -0,0 +1,2560 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ +// Reversing +// -------------------------------------------------------------------------- +/** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str + * the String to reverse + * @return the reversed String + */ +public static java.lang.String reverse(java.lang.String str) { + { + return new java.lang.StringBuffer(/* NPEX_NULL_EXP */ + str).reverse().toString(); + } +} + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_1930/metadata.json b/Java/maven-shared-utils-StringUtils_1930/metadata.json new file mode 100644 index 000000000..7ab5d1e05 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1930/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_1930", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1942, + "npe_method": "reverse", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_1930" + } +} diff --git a/Java/maven-shared-utils-StringUtils_1930/npe.json b/Java/maven-shared-utils-StringUtils_1930/npe.json new file mode 100644 index 000000000..adfd28543 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_1930/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 1942, + "npe_method": "reverse", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_228/Dockerfile b/Java/maven-shared-utils-StringUtils_228/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_228/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_228/buggy.java b/Java/maven-shared-utils-StringUtils_228/buggy.java new file mode 100644 index 000000000..483808032 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_228/buggy.java @@ -0,0 +1,2570 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated +// Equals and IndexOf +// -------------------------------------------------------------------------- +/** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 + * the first string + * @param str2 + * the second string + * @return true if the Strings are equal, case sensitive, or +both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ +public static boolean equals(@javax.annotation.Nullable +java.lang.String str1, @javax.annotation.Nullable +java.lang.String str2) { + return /* NPEX_NULL_EXP */ + str1.equals(str2); +} + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_228/metadata.json b/Java/maven-shared-utils-StringUtils_228/metadata.json new file mode 100644 index 000000000..fce72737d --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_228/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_228", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 247, + "npe_method": "equals", + "deref_field": "str1", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_228" + } +} diff --git a/Java/maven-shared-utils-StringUtils_228/npe.json b/Java/maven-shared-utils-StringUtils_228/npe.json new file mode 100644 index 000000000..c85f89bfc --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_228/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 247, + "npe_method": "equals", + "deref_field": "str1", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_2298/Dockerfile b/Java/maven-shared-utils-StringUtils_2298/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2298/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_2298/buggy.java b/Java/maven-shared-utils-StringUtils_2298/buggy.java new file mode 100644 index 000000000..f1a3d7c76 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2298/buggy.java @@ -0,0 +1,2553 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ +/** + * + * + * @param source + * the source + * @param quoteChar + * the quote character + * @param escapedChars + * set of characters to escape + * @param quotingTriggers + * the quoting trigger + * @param escapeChar + * prefix for escaping a character + * @param force + * true/false + * @return the String quoted and escaped + */ +public static java.lang.String quoteAndEscape(@javax.annotation.Nullable +java.lang.String source, char quoteChar, @javax.annotation.Nonnull +final char[] escapedChars, @javax.annotation.Nonnull +final char[] quotingTriggers, char escapeChar, boolean force) { + { + if (((!force) && /* NPEX_NULL_EXP */ + source.startsWith(java.lang.Character.toString(quoteChar))) && source.endsWith(java.lang.Character.toString(quoteChar))) { + return source; + } + java.lang.String escaped = org.apache.maven.shared.utils.StringUtils.escape(source, escapedChars, escapeChar); + boolean quote = false; + if (force) { + quote = true; + } else if (!escaped.equals(source)) { + quote = true; + } else { + for (char quotingTrigger : quotingTriggers) { + if (escaped.indexOf(quotingTrigger) > (-1)) { + quote = true; + break; + } + } + } + if (quote) { + return (quoteChar + escaped) + quoteChar; + } + return escaped; + } +} + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_2298/metadata.json b/Java/maven-shared-utils-StringUtils_2298/metadata.json new file mode 100644 index 000000000..77b12d9f5 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2298/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_2298", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 2318, + "npe_method": "quoteAndEscape", + "deref_field": "source", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_2298" + } +} diff --git a/Java/maven-shared-utils-StringUtils_2298/npe.json b/Java/maven-shared-utils-StringUtils_2298/npe.json new file mode 100644 index 000000000..cc2f3f257 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2298/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 2318, + "npe_method": "quoteAndEscape", + "deref_field": "source", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_2348/Dockerfile b/Java/maven-shared-utils-StringUtils_2348/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2348/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_2348/buggy.java b/Java/maven-shared-utils-StringUtils_2348/buggy.java new file mode 100644 index 000000000..b1e4cabcb --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2348/buggy.java @@ -0,0 +1,2555 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ +/** + * + * + * @param source + * the source + * @param escapedChars + * set of characters to escape + * @param escapeChar + * prefix for escaping a character + * @return the String escaped + */ +public static java.lang.String escape(@javax.annotation.Nullable +java.lang.String source, @javax.annotation.Nonnull +final char[] escapedChars, char escapeChar) { + { + char[] eqc = new char[escapedChars.length]; + java.lang.System.arraycopy(escapedChars, 0, eqc, 0, escapedChars.length); + java.util.Arrays.sort(eqc); + java.lang.StringBuilder buffer = new java.lang.StringBuilder(/* NPEX_NULL_EXP */ + source.length()); + for (int i = 0; i < source.length(); i++) { + final char c = source.charAt(i); + int result = java.util.Arrays.binarySearch(eqc, c); + if (result > (-1)) { + buffer.append(escapeChar); + } + buffer.append(c); + } + return buffer.toString(); + } +} + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_2348/metadata.json b/Java/maven-shared-utils-StringUtils_2348/metadata.json new file mode 100644 index 000000000..04c123d61 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2348/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_2348", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 2365, + "npe_method": "escape", + "deref_field": "source", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_2348" + } +} diff --git a/Java/maven-shared-utils-StringUtils_2348/npe.json b/Java/maven-shared-utils-StringUtils_2348/npe.json new file mode 100644 index 000000000..257b2fc61 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2348/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 2365, + "npe_method": "escape", + "deref_field": "source", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_2425/Dockerfile b/Java/maven-shared-utils-StringUtils_2425/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2425/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_2425/buggy.java b/Java/maven-shared-utils-StringUtils_2425/buggy.java new file mode 100644 index 000000000..11454261e --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2425/buggy.java @@ -0,0 +1,2548 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ +/** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s + * a not null String + * @param ls + * the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException + * if ls is not '\n', '\r' and '\r\n' characters. + */ +public static java.lang.String unifyLineSeparators(@javax.annotation.Nullable +java.lang.String s, @javax.annotation.Nullable +java.lang.String ls) { + { + if (ls == null) { + ls = java.lang.System.getProperty("line.separator"); + } + if (!((ls.equals("\n") || ls.equals("\r")) || ls.equals("\r\n"))) { + throw new java.lang.IllegalArgumentException("Requested line separator is invalid."); + } + int length = /* NPEX_NULL_EXP */ + s.length(); + java.lang.StringBuilder buffer = new java.lang.StringBuilder(length); + for (int i = 0; i < length; i++) { + if (s.charAt(i) == '\r') { + if (((i + 1) < length) && (s.charAt(i + 1) == '\n')) { + i++; + } + buffer.append(ls); + } else if (s.charAt(i) == '\n') { + buffer.append(ls); + } else { + buffer.append(s.charAt(i)); + } + } + return buffer.toString(); + } +} + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_2425/metadata.json b/Java/maven-shared-utils-StringUtils_2425/metadata.json new file mode 100644 index 000000000..b2d100270 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2425/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_2425", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 2446, + "npe_method": "unifyLineSeparators", + "deref_field": "s", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_2425" + } +} diff --git a/Java/maven-shared-utils-StringUtils_2425/npe.json b/Java/maven-shared-utils-StringUtils_2425/npe.json new file mode 100644 index 000000000..78eb96f64 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_2425/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 2446, + "npe_method": "unifyLineSeparators", + "deref_field": "s", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_246/Dockerfile b/Java/maven-shared-utils-StringUtils_246/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_246/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_246/buggy.java b/Java/maven-shared-utils-StringUtils_246/buggy.java new file mode 100644 index 000000000..f0b21e203 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_246/buggy.java @@ -0,0 +1,2566 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ +/** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 + * the first string + * @param str2 + * the second string + * @return true if the Strings are equal, case insensitive, or +both null + * @see java.lang.String#equalsIgnoreCase(String) + */ +public static boolean equalsIgnoreCase(java.lang.String str1, java.lang.String str2) { + return /* NPEX_NULL_EXP */ + str1.equalsIgnoreCase(str2); +} + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_246/metadata.json b/Java/maven-shared-utils-StringUtils_246/metadata.json new file mode 100644 index 000000000..6683de06d --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_246/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_246", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 261, + "npe_method": "equalsIgnoreCase", + "deref_field": "str1", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_246" + } +} diff --git a/Java/maven-shared-utils-StringUtils_246/npe.json b/Java/maven-shared-utils-StringUtils_246/npe.json new file mode 100644 index 000000000..57bb95740 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_246/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 261, + "npe_method": "equalsIgnoreCase", + "deref_field": "str1", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_331/Dockerfile b/Java/maven-shared-utils-StringUtils_331/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_331/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_331/buggy.java b/Java/maven-shared-utils-StringUtils_331/buggy.java new file mode 100644 index 000000000..1a74b8bdb --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_331/buggy.java @@ -0,0 +1,2559 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ +// Substring +// -------------------------------------------------------------------------- +/** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str + * the String to get the substring from + * @param start + * the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ +public static java.lang.String substring(java.lang.String str, int start) { + { + // handle negatives, which means last n characters + if (start < 0) { + start = /* NPEX_NULL_EXP */ + str.length() + start;// remember start is negative + + } + if (start < 0) { + start = 0; + } + if (start > str.length()) { + return ""; + } + return str.substring(start); + } +} + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_331/metadata.json b/Java/maven-shared-utils-StringUtils_331/metadata.json new file mode 100644 index 000000000..a9343e631 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_331/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_331", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 349, + "npe_method": "substring", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_331" + } +} diff --git a/Java/maven-shared-utils-StringUtils_331/npe.json b/Java/maven-shared-utils-StringUtils_331/npe.json new file mode 100644 index 000000000..1f6f92a29 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_331/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 349, + "npe_method": "substring", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_369/Dockerfile b/Java/maven-shared-utils-StringUtils_369/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_369/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_369/buggy.java b/Java/maven-shared-utils-StringUtils_369/buggy.java new file mode 100644 index 000000000..e2be46c54 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_369/buggy.java @@ -0,0 +1,2556 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ +/** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str + * the String to get the substring from + * @param start + * the position to start from, negative means + * count back from the end of the string by this many characters + * @param end + * the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ +public static java.lang.String substring(java.lang.String str, int start, int end) { + { + // handle negatives + if (end < 0) { + end = /* NPEX_NULL_EXP */ + str.length() + end;// remember end is negative + + } + if (start < 0) { + start = str.length() + start;// remember start is negative + + } + // check length next + if (end > str.length()) { + // check this works. + end = str.length(); + } + // if start is greater than end, return "" + if (start > end) { + return ""; + } + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + return str.substring(start, end); + } +} + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_369/metadata.json b/Java/maven-shared-utils-StringUtils_369/metadata.json new file mode 100644 index 000000000..e2ae65794 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_369/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_369", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 388, + "npe_method": "substring", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_369" + } +} diff --git a/Java/maven-shared-utils-StringUtils_369/npe.json b/Java/maven-shared-utils-StringUtils_369/npe.json new file mode 100644 index 000000000..35118f070 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_369/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 388, + "npe_method": "substring", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_489/Dockerfile b/Java/maven-shared-utils-StringUtils_489/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_489/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_489/buggy.java b/Java/maven-shared-utils-StringUtils_489/buggy.java new file mode 100644 index 000000000..f9de0cd79 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_489/buggy.java @@ -0,0 +1,2563 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ +/** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str + * the String to get the characters from + * @param pos + * the position to start from + * @param len + * the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException + * if pos is out of bounds + * @throws IllegalArgumentException + * if len is less than zero + */ +public static java.lang.String mid(java.lang.String str, int pos, int len) { + if ((pos < 0) || ((str != null) && (pos > str.length()))) { + throw new java.lang.StringIndexOutOfBoundsException(("String index " + pos) + " is out of bounds"); + } + if (len < 0) { + throw new java.lang.IllegalArgumentException(("Requested String length " + len) + " is less than zero"); + } + { + if (/* NPEX_NULL_EXP */ + str.length() <= (pos + len)) { + return str.substring(pos); + } else { + return str.substring(pos, pos + len); + } + } +} + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_489/metadata.json b/Java/maven-shared-utils-StringUtils_489/metadata.json new file mode 100644 index 000000000..6c88fa338 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_489/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_489", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 507, + "npe_method": "mid", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_489" + } +} diff --git a/Java/maven-shared-utils-StringUtils_489/npe.json b/Java/maven-shared-utils-StringUtils_489/npe.json new file mode 100644 index 000000000..cb34fd41e --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_489/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 507, + "npe_method": "mid", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_552/Dockerfile b/Java/maven-shared-utils-StringUtils_552/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_552/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_552/buggy.java b/Java/maven-shared-utils-StringUtils_552/buggy.java new file mode 100644 index 000000000..4b38e285b --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_552/buggy.java @@ -0,0 +1,2560 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ +/** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str + * the string to parse + * @param separator + * Characters used as the delimiters. If + * null, splits on whitespace. + * @param max + * the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ +public static java.lang.String[] split(@javax.annotation.Nonnull +java.lang.String str, @javax.annotation.Nullable +java.lang.String separator, int max) { + java.util.StringTokenizer tok; + { + tok = new java.util.StringTokenizer(str, /* NPEX_NULL_EXP */ + separator); + } + int listSize = tok.countTokens(); + if ((max > 0) && (listSize > max)) { + listSize = max; + } + java.lang.String[] list = new java.lang.String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while (tok.hasMoreTokens()) { + if ((max > 0) && (i == (listSize - 1))) { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + java.lang.String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf(endToken, lastTokenEnd); + list[i] = str.substring(lastTokenBegin); + break; + } else { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf(list[i], lastTokenEnd); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; +} + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_552/metadata.json b/Java/maven-shared-utils-StringUtils_552/metadata.json new file mode 100644 index 000000000..c505ab6ae --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_552/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_552", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 576, + "npe_method": "split", + "deref_field": "separator", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_552" + } +} diff --git a/Java/maven-shared-utils-StringUtils_552/npe.json b/Java/maven-shared-utils-StringUtils_552/npe.json new file mode 100644 index 000000000..da42bc745 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_552/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 576, + "npe_method": "split", + "deref_field": "separator", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_82/Dockerfile b/Java/maven-shared-utils-StringUtils_82/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_82/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_82/buggy.java b/Java/maven-shared-utils-StringUtils_82/buggy.java new file mode 100644 index 000000000..5b5466ab2 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_82/buggy.java @@ -0,0 +1,2563 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ +// Empty +// -------------------------------------------------------------------------- +/** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str + * the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ +public static java.lang.String clean(java.lang.String str) { + return /* NPEX_NULL_EXP */ + str.trim(); +} + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ + public static String trim( String str ) + { + return ( str == null ? null : str.trim() ); + } + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_82/metadata.json b/Java/maven-shared-utils-StringUtils_82/metadata.json new file mode 100644 index 000000000..5e4c6e11f --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_82/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_82", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 94, + "npe_method": "clean", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_82" + } +} diff --git a/Java/maven-shared-utils-StringUtils_82/npe.json b/Java/maven-shared-utils-StringUtils_82/npe.json new file mode 100644 index 000000000..9f70766ff --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_82/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 94, + "npe_method": "clean", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-StringUtils_96/Dockerfile b/Java/maven-shared-utils-StringUtils_96/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_96/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-StringUtils_96/buggy.java b/Java/maven-shared-utils-StringUtils_96/buggy.java new file mode 100644 index 000000000..615690b8d --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_96/buggy.java @@ -0,0 +1,2561 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + *

          Common String manipulation routines.

          + * + *

          Originally from + * Turbine, the + * GenerationJavaCore library and Velocity. + * Later a lots methods from commons-lang StringUtils + * got added too. Gradually smaller additions and fixes have been made + * over the time by various ASF committers.

          + * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * + */ +public class StringUtils +{ + /** + *

          StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

          + * + *

          This constructor is public to permit tools that require a JavaBean + * manager to operate.

          + */ + public StringUtils() + { + } + + // Empty + //-------------------------------------------------------------------------- + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * an empty String.

          + * + * @param str the String to check + * @return the trimmed text (never null) + * @see java.lang.String#trim() + */ + @Nonnull public static String clean( String str ) + { + return ( str == null ? "" : str.trim() ); + } + + /** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ +/** + *

          Removes C0 control characters, including ASCII whitespace, from both + * ends of this String, handling null by returning + * null.

          + * + * @param str + * the String to check + * @return the trimmed text (or null) + * @see java.lang.String#trim() + */ +public static java.lang.String trim(java.lang.String str) { + return /* NPEX_NULL_EXP */ + str.trim(); +} + + /** + *

          Deletes all whitespace from a String.

          + * + *

          Whitespace is defined by + * {@link Character#isWhitespace(char)}.

          + * + * @param str String target to delete whitespace from + * @return the String without whitespace + * @throws NullPointerException + */ + @Nonnull public static String deleteWhitespace( @Nonnull String str ) + { + StringBuilder buffer = new StringBuilder(); + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + buffer.append( str.charAt( i ) ); + } + } + return buffer.toString(); + } + + /** + *

          Checks if a String is non null and is + * not empty (length > 0).

          + * + * @param str the String to check + * @return true if the String is non-null, and not length zero + */ + public static boolean isNotEmpty( @Nullable String str ) + { + return ( ( str != null ) && ( str.length() > 0 ) ); + } + + /** + *

          Checks if a (trimmed) String is null or empty.

          + * + *

          Note: In future releases, this method will no longer trim the input string such that it works + * complementary to {@link #isNotEmpty(String)}. Code that wants to test for whitespace-only strings should be + * migrated to use {@link #isBlank(String)} instead.

          + * + * @param str the String to check + * @return true if the String is null, or + * length zero once trimmed + */ + public static boolean isEmpty( @Nullable String str ) + { + return ( ( str == null ) || ( str.trim().length() == 0 ) ); + } + + /** + *

          + * Checks if a String is whitespace, empty ("") or null. + *

          + * + *
          +     * StringUtils.isBlank(null)      = true
          +     * StringUtils.isBlank("")        = true
          +     * StringUtils.isBlank(" ")       = true
          +     * StringUtils.isBlank("bob")     = false
          +     * StringUtils.isBlank("  bob  ") = false
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is null, empty or whitespace + * + */ + public static boolean isBlank( @Nullable String str ) + { + int strLen; + // CHECKSTYLE_OFF: InnerAssignment + if ( str == null || ( strLen = str.length() ) == 0 ) + // CHECKSTYLE_ON: InnerAssignment + { + return true; + } + for ( int i = 0; i < strLen; i++ ) + { + if ( !Character.isWhitespace( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          + * Checks if a String is not empty (""), not null and not whitespace only. + *

          + * + *
          +     * StringUtils.isNotBlank(null)      = false
          +     * StringUtils.isNotBlank("")        = false
          +     * StringUtils.isNotBlank(" ")       = false
          +     * StringUtils.isNotBlank("bob")     = true
          +     * StringUtils.isNotBlank("  bob  ") = true
          +     * 
          + * + * @param str the String to check, may be null + * @return true if the String is not empty and not null and not whitespace + * + */ + public static boolean isNotBlank( @Nullable String str ) + { + return !isBlank( str ); + } + + // Equals and IndexOf + //-------------------------------------------------------------------------- + + /** + *

          Compares two Strings, returning true if they are equal.

          + * + *

          nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case sensitive, or + * both null + * @see java.lang.String#equals(Object) + * @deprecated use {@code java.lang.Objects.equals()} + */ + @Deprecated + public static boolean equals( @Nullable String str1, @Nullable String str2 ) + { + return ( str1 == null ? str2 == null : str1.equals( str2 ) ); + } + + /** + *

          Compares two Strings, returning true if they are equal ignoring + * the case.

          + * + *

          Nulls are handled without exceptions. Two null + * references are considered equal. Comparison is case insensitive.

          + * + * @param str1 the first string + * @param str2 the second string + * @return true if the Strings are equal, case insensitive, or + * both null + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static boolean equalsIgnoreCase( String str1, String str2 ) + { + return ( str1 == null ? str2 == null : str1.equalsIgnoreCase( str2 ) ); + } + + /** + *

          Find the first index of any of a set of potential substrings.

          + * + *

          null String will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the first index of any of the searchStrs in str + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int indexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.indexOf( searchStr ); + if ( tmp == -1 ) + { + continue; + } + + if ( tmp < ret ) + { + ret = tmp; + } + } + + return ( ret == Integer.MAX_VALUE ) ? -1 : ret; + } + + /** + *

          Find the latest index of any of a set of potential substrings.

          + * + *

          null string will return -1.

          + * + * @param str the String to check + * @param searchStrs the Strings to search for + * @return the last index of any of the Strings + * @throws NullPointerException if any of searchStrs[i] is null + */ + public static int lastIndexOfAny( String str, String... searchStrs ) + { + if ( ( str == null ) || ( searchStrs == null ) ) + { + return -1; + } + int ret = -1; + int tmp; + for ( String searchStr : searchStrs ) + { + tmp = str.lastIndexOf( searchStr ); + if ( tmp > ret ) + { + ret = tmp; + } + } + return ret; + } + + // Substring + //-------------------------------------------------------------------------- + + /** + *

          Gets a substring from the specified string avoiding exceptions.

          + * + *

          A negative start position can be used to start n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position + */ + public static String substring( String str, int start ) + { + if ( str == null ) + { + return null; + } + + // handle negatives, which means last n characters + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + if ( start < 0 ) + { + start = 0; + } + if ( start > str.length() ) + { + return ""; + } + + return str.substring( start ); + } + + /** + *

          Gets a substring from the specified String avoiding exceptions.

          + * + *

          A negative start position can be used to start/end n + * characters from the end of the String.

          + * + * @param str the String to get the substring from + * @param start the position to start from, negative means + * count back from the end of the string by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position + */ + public static String substring( String str, int start, int end ) + { + if ( str == null ) + { + return null; + } + + // handle negatives + if ( end < 0 ) + { + end = str.length() + end; // remember end is negative + } + if ( start < 0 ) + { + start = str.length() + start; // remember start is negative + } + + // check length next + if ( end > str.length() ) + { + // check this works. + end = str.length(); + } + + // if start is greater than end, return "" + if ( start > end ) + { + return ""; + } + + if ( start < 0 ) + { + start = 0; + } + if ( end < 0 ) + { + end = 0; + } + + return str.substring( start, end ); + } + + /** + *

          Gets the leftmost n characters of a String.

          + * + *

          If n characters are not available, or the + * String is null, the String will be returned without + * an exception.

          + * + * @param str the String to get the leftmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String left( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( 0, len ); + } + } + + /** + *

          Gets the rightmost n characters of a String.

          + * + *

          If n characters are not available, or the String + * is null, the String will be returned without an + * exception.

          + * + * @param str the String to get the rightmost characters from + * @param len the length of the required String + * @return the leftmost characters + * @throws IllegalArgumentException if len is less than zero + */ + public static String right( String str, int len ) + { + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( ( str == null ) || ( str.length() <= len ) ) + { + return str; + } + else + { + return str.substring( str.length() - len ); + } + } + + /** + *

          Gets n characters from the middle of a String.

          + * + *

          If n characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is null, null will be returned.

          + * + * @param str the String to get the characters from + * @param pos the position to start from + * @param len the length of the required String + * @return the leftmost characters + * @throws IndexOutOfBoundsException if pos is out of bounds + * @throws IllegalArgumentException if len is less than zero + */ + public static String mid( String str, int pos, int len ) + { + if ( ( pos < 0 ) || ( ( str != null ) && ( pos > str.length() ) ) ) + { + throw new StringIndexOutOfBoundsException( "String index " + pos + " is out of bounds" ); + } + if ( len < 0 ) + { + throw new IllegalArgumentException( "Requested String length " + len + " is less than zero" ); + } + if ( str == null ) + { + return null; + } + if ( str.length() <= ( pos + len ) ) + { + return str.substring( pos ); + } + else + { + return str.substring( pos, pos + len ); + } + } + + // Splitting + //-------------------------------------------------------------------------- + + /** + *

          Splits the provided text into a array, using whitespace as the + * separator.

          + * + *

          The separator is not included in the returned String array.

          + * + * @param str the String to parse + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str ) + { + return split( str, null, -1 ); + } + + /** + * @param text the text to be split + * @param separator the separator at which the text will be split + * @return the resulting array + * @see #split(String, String, int) + */ + @Nonnull public static String[] split( @Nonnull String text, @Nullable String separator ) + { + return split( text, separator, -1 ); + } + + /** + *

          Splits the provided text into a array, based on a given separator.

          + * + *

          The separator is not included in the returned String array. The + * maximum number of splits to perform can be controlled. A null + * separator causes splitting on whitespace.

          + * + *

          This is useful for quickly splitting a String into + * an array of tokens, instead of an enumeration of tokens (as + * StringTokenizer does).

          + * + * @param str the string to parse + * @param separator Characters used as the delimiters. If + * null, splits on whitespace. + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings + */ + @Nonnull public static String[] split( @Nonnull String str, @Nullable String separator, int max ) + { + StringTokenizer tok; + if ( separator == null ) + { + // Null separator means we're using StringTokenizer's default + // delimiter, which comprises all whitespace characters. + tok = new StringTokenizer( str ); + } + else + { + tok = new StringTokenizer( str, separator ); + } + + int listSize = tok.countTokens(); + if ( ( max > 0 ) && ( listSize > max ) ) + { + listSize = max; + } + + String[] list = new String[listSize]; + int i = 0; + int lastTokenBegin; + int lastTokenEnd = 0; + while ( tok.hasMoreTokens() ) + { + if ( ( max > 0 ) && ( i == listSize - 1 ) ) + { + // In the situation where we hit the max yet have + // tokens left over in our input, the last list + // element gets all remaining text. + String endToken = tok.nextToken(); + lastTokenBegin = str.indexOf( endToken, lastTokenEnd ); + list[i] = str.substring( lastTokenBegin ); + break; + } + else + { + list[i] = tok.nextToken(); + lastTokenBegin = str.indexOf( list[i], lastTokenEnd ); + lastTokenEnd = lastTokenBegin + list[i].length(); + } + i++; + } + return list; + } + + // Joining + //-------------------------------------------------------------------------- + + /** + *

          Concatenates elements of an array into a single String.

          + * + *

          The difference from join is that concatenate has no delimiter.

          + * + * @param array the array of values to concatenate. + * @return the concatenated string. + */ + @Nonnull public static String concatenate( @Nonnull Object... array ) + { + return join( array, "" ); + } + + /** + *

          Joins the elements of the provided array into a single String + * containing the provided list of elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param array the array of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Object[] array, @Nullable String separator ) + { + if ( separator == null ) + { + separator = ""; + } + int arraySize = array.length; + int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); + StringBuilder buf = new StringBuilder( bufSize ); + + for ( int i = 0; i < arraySize; i++ ) + { + if ( i > 0 ) + { + buf.append( separator ); + } + buf.append( array[i] ); + } + return buf.toString(); + } + + /** + *

          Joins the elements of the provided Iterator into + * a single String containing the provided elements.

          + * + *

          No delimiter is added before or after the list. A + * null separator is the same as a blank String.

          + * + * @param iterator the Iterator of values to join together + * @param separator the separator character to use + * @return the joined String + */ + @Nonnull public static String join( @Nonnull Iterator iterator, String separator ) + { + if ( separator == null ) + { + separator = ""; + } + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small + while ( iterator.hasNext() ) + { + buf.append( iterator.next() ); + if ( iterator.hasNext() ) + { + buf.append( separator ); + } + } + return buf.toString(); + } + + // Replacing + //-------------------------------------------------------------------------- + + /** + *

          Replace a char with another char inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replaceOnce( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurances of a char within another char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @return the text with any replacements processed + * @see #replace(String text, char repl, char with, int max) + */ + public static String replace( @Nullable String text, char repl, char with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a char with another char inside a larger String, + * for the first max values of the search char.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl char to search for + * @param with char to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, char repl, char with, int max ) + { + return replace( text, String.valueOf( repl ), String.valueOf( with ), max ); + } + + /** + *

          Replace a String with another String inside a larger String, once.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replaceOnce( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, 1 ); + } + + /** + *

          Replace all occurrences of a String within another String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @return the text with any replacements processed + * @see #replace(String text, String repl, String with, int max) + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with ) + { + return replace( text, repl, with, -1 ); + } + + /** + *

          Replace a String with another String inside a larger String, + * for the first max values of the search String.

          + * + *

          A null reference passed to this method is a no-op.

          + * + * @param text text to search and replace in + * @param repl String to search for + * @param with String to replace with + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed + */ + public static String replace( @Nullable String text, @Nullable String repl, @Nullable String with, int max ) + { + if ( ( text == null ) || ( repl == null ) || ( with == null ) || ( repl.length() == 0 ) ) + { + return text; + } + + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text, start, end ).append( with ); + start = end + repl.length(); + + if ( --max == 0 ) + { + break; + } + } + buf.append( text, start, text.length() ); + return buf.toString(); + } + + /** + *

          Overlay a part of a String with another String.

          + * + * @param text String to do overlaying in + * @param overlay String to overlay + * @param start position to start overlaying at + * @param end position to stop overlaying before + * @return String with overlaid text + * @throws NullPointerException if text or overlay is null + */ + @Nonnull public static String overlayString( @Nonnull String text, @Nonnull String overlay, int start, int end ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay is null" ); + } + return new StringBuilder( start + overlay.length() + text.length() - end + 1 ) + .append( text, 0, start ) + .append( overlay ) + .append( text, end, text.length() ) + .toString(); + } + + // Centering + //-------------------------------------------------------------------------- + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses spaces as the value to buffer the String with. + * Equivalent to center(str, size, " ").

          + * + * @param str String to center + * @param size int size of new String + * @return String containing centered String + * @throws NullPointerException if str is null + */ + @Nonnull public static String center( @Nonnull String str, int size ) + { + return center( str, size, " " ); + } + + /** + *

          Center a String in a larger String of size n.

          + * + *

          Uses a supplied String as the value to buffer the String with.

          + * + * @param str String to center + * @param size int size of new String + * @param delim String to buffer the new String with + * @return String containing centered String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String center( @Nonnull String str, int size, @Nonnull String delim ) + { + int sz = str.length(); + int p = size - sz; + if ( p < 1 ) + { + return str; + } + str = leftPad( str, sz + p / 2, delim ); + str = rightPad( str, size, delim ); + return str; + } + + // Chomping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last newline, and everything after it from a String.

          + * + * @param str String to chomp the newline from + * @return String without chomped newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chomp( @Nonnull String str ) + { + return chomp( str, "\n" ); + } + + /** + *

          Remove the last value of a supplied String, and everything after + * it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx ); + } + else + { + return str; + } + } + + /** + *

          Remove a newline if and only if it is at the end + * of the supplied String.

          + * + * @param str String to chomp from + * @return String without chomped ending + * @throws NullPointerException if str is null + */ + @Nonnull public static String chompLast( @Nonnull String str ) + { + return chompLast( str, "\n" ); + } + + /** + *

          Remove a value if and only if the String ends with that value.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped ending + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String chompLast( @Nonnull String str, @Nonnull String sep ) + { + if ( str.length() == 0 ) + { + return str; + } + String sub = str.substring( str.length() - sep.length() ); + if ( sep.equals( sub ) ) + { + return str.substring( 0, str.length() - sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove everything and return the last value of a supplied String, and + * everything after it from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String chomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getChomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.lastIndexOf( sep ); + if ( idx == str.length() - sep.length() ) + { + return sep; + } + else if ( idx != -1 ) + { + return str.substring( idx ); + } + else + { + return ""; + } + } + + /** + *

          Remove the first value of a supplied String, and everything before it + * from a String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String without chomped beginning + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String prechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( idx + sep.length() ); + } + else + { + return str; + } + } + + /** + *

          Remove and return everything before the first value of a + * supplied String from another String.

          + * + * @param str String to chomp from + * @param sep String to chomp + * @return String prechomped + * @throws NullPointerException if str or sep is null + */ + @Nonnull public static String getPrechomp( @Nonnull String str, @Nonnull String sep ) + { + int idx = str.indexOf( sep ); + if ( idx != -1 ) + { + return str.substring( 0, idx + sep.length() ); + } + else + { + return ""; + } + } + + // Chopping + //-------------------------------------------------------------------------- + + /** + *

          Remove the last character from a String.

          + * + *

          If the String ends in \r\n, then remove both + * of them.

          + * + * @param str String to chop last character from + * @return String without last character + * @throws NullPointerException if str is null + */ + @Nonnull public static String chop( @Nonnull String str ) + { + if ( "".equals( str ) ) + { + return ""; + } + if ( str.length() == 1 ) + { + return ""; + } + int lastIdx = str.length() - 1; + String ret = str.substring( 0, lastIdx ); + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( ret.charAt( lastIdx - 1 ) == '\r' ) + { + return ret.substring( 0, lastIdx - 1 ); + } + } + return ret; + } + + /** + *

          Remove \n from end of a String if it's there. + * If a \r precedes it, then remove that too.

          + * + * @param str String to chop a newline from + * @return String without newline + * @throws NullPointerException if str is null + */ + @Nonnull public static String chopNewline( @Nonnull String str ) + { + int lastIdx = str.length() - 1; + char last = str.charAt( lastIdx ); + if ( last == '\n' ) + { + if ( str.charAt( lastIdx - 1 ) == '\r' ) + { + lastIdx--; + } + } + else + { + lastIdx++; + } + return str.substring( 0, lastIdx ); + } + + // Conversion + //-------------------------------------------------------------------------- + + // spec 3.10.6 + + /** + *

          Escapes any values it finds into their String form.

          + * + *

          So a tab becomes the characters '\\' and + * 't'.

          + * + * @param str String to escape values in + * @return String with escaped values + * @throws NullPointerException if str is null + */ + @Nonnull public static String escape( @Nonnull String str ) + { + // improved with code from cybertiger@cyberiantiger.org + // unicode from him, and defaul for < 32's. + int sz = str.length(); + StringBuilder buffer = new StringBuilder( 2 * sz ); + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + + // handle unicode + // CHECKSTYLE_OFF: MagicNumber + if ( ch > 0xfff ) + { + buffer.append( "\\u" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0xff ) + { + buffer.append( "\\u0" ).append( Integer.toHexString( ch ) ); + } + else if ( ch > 0x7f ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + // CHECKSTYLE_ON: MagicNumber + else if ( ch < 32 ) + { + switch ( ch ) + { + case '\b': + buffer.append( '\\' ); + buffer.append( 'b' ); + break; + case '\n': + buffer.append( '\\' ); + buffer.append( 'n' ); + break; + case '\t': + buffer.append( '\\' ); + buffer.append( 't' ); + break; + case '\f': + buffer.append( '\\' ); + buffer.append( 'f' ); + break; + case '\r': + buffer.append( '\\' ); + buffer.append( 'r' ); + break; + default: + if ( ch > 0xf ) + { + buffer.append( "\\u00" ).append( Integer.toHexString( ch ) ); + } + else + { + buffer.append( "\\u000" ).append( Integer.toHexString( ch ) ); + } + break; + } + } + else + { + switch ( ch ) + { + case '\'': + buffer.append( '\\' ); + buffer.append( '\'' ); + break; + case '"': + buffer.append( '\\' ); + buffer.append( '"' ); + break; + case '\\': + buffer.append( '\\' ); + buffer.append( '\\' ); + break; + default: + buffer.append( ch ); + break; + } + } + } + return buffer.toString(); + } + + // Padding + //-------------------------------------------------------------------------- + + /** + *

          Repeat a String n times to form a + * new string.

          + * + * @param str String to repeat + * @param repeat number of times to repeat str + * @return String with repeated String + * @throws NegativeArraySizeException if repeat < 0 + * @throws NullPointerException if str is null + */ + @Nonnull public static String repeat( @Nonnull String str, int repeat ) + { + StringBuilder buffer = new StringBuilder( repeat * str.length() ); + for ( int i = 0; i < repeat; i++ ) + { + buffer.append( str ); + } + return buffer.toString(); + } + + /** + *

          Right pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to repeat + * @param size number of times to repeat str + * @return right padded String + * @throws NullPointerException if str is null + */ + @Nonnull public static String rightPad( @Nonnull String str, int size ) + { + return rightPad( str, size, " " ); + } + + /** + *

          Right pad a String with a specified string.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return right padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty String + */ + @Nonnull public static String rightPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str += repeat( delim, size ); + } + return str; + } + + /** + *

          Left pad a String with spaces.

          + * + *

          The String is padded to the size of n.

          + * + * @param str String to pad out + * @param size size to pad to + * @return left padded String + * @throws NullPointerException if str or delim is null + */ + @Nonnull public static String leftPad( @Nonnull String str, int size ) + { + return leftPad( str, size, " " ); + } + + /** + * Left pad a String with a specified string. Pad to a size of n. + * + * @param str String to pad out + * @param size size to pad to + * @param delim String to pad with + * @return left padded String + * @throws NullPointerException if str or delim is null + * @throws ArithmeticException if delim is the empty string + */ + @Nonnull public static String leftPad( @Nonnull String str, int size, @Nonnull String delim ) + { + size = ( size - str.length() ) / delim.length(); + if ( size > 0 ) + { + str = repeat( delim, size ) + str; + } + return str; + } + + // Stripping + //-------------------------------------------------------------------------- + + /** + *

          Remove whitespace from the front and back of a String.

          + * + * @param str the String to remove whitespace from + * @return the stripped String + */ + public static String strip( String str ) + { + return strip( str, null ); + } + + /** + *

          Remove a specified String from the front and back of a + * String.

          + * + *

          If whitespace is wanted to be removed, used the + * {@link #strip(java.lang.String)} method.

          + * + * @param str the String to remove a string from + * @param delim the String to remove at start and end + * @return the stripped String + */ + public static String strip( String str, @Nullable String delim ) + { + str = stripStart( str, delim ); + return stripEnd( str, delim ); + } + + /** + *

          Strip whitespace from the front and back of every String + * in the array.

          + * + * @param strs the Strings to remove whitespace from + * @return the stripped Strings + */ + public static String[] stripAll( String... strs ) + { + return stripAll( strs, null ); + } + + /** + *

          Strip the specified delimiter from the front and back of + * every String in the array.

          + * + * @param strs the Strings to remove a String from + * @param delimiter the String to remove at start and end + * @return the stripped Strings + */ + public static String[] stripAll( String[] strs, @Nullable String delimiter ) + { + if ( ( strs == null ) || ( strs.length == 0 ) ) + { + return strs; + } + int sz = strs.length; + String[] newArr = new String[sz]; + for ( int i = 0; i < sz; i++ ) + { + newArr[i] = strip( strs[i], delimiter ); + } + return newArr; + } + + /** + *

          Strip any of a supplied String from the end of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripEnd( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + int end = str.length(); + + if ( strip == null ) + { + while ( ( end != 0 ) && Character.isWhitespace( str.charAt( end - 1 ) ) ) + { + end--; + } + } + else + { + while ( ( end != 0 ) && ( strip.indexOf( str.charAt( end - 1 ) ) != -1 ) ) + { + end--; + } + } + return str.substring( 0, end ); + } + + /** + *

          Strip any of a supplied String from the start of a String.

          + * + *

          If the strip String is null, whitespace is + * stripped.

          + * + * @param str the String to remove characters from + * @param strip the String to remove + * @return the stripped String + */ + public static String stripStart( String str, @Nullable String strip ) + { + if ( str == null ) + { + return null; + } + + int start = 0; + + int sz = str.length(); + + if ( strip == null ) + { + while ( ( start != sz ) && Character.isWhitespace( str.charAt( start ) ) ) + { + start++; + } + } + else + { + while ( ( start != sz ) && ( strip.indexOf( str.charAt( start ) ) != -1 ) ) + { + start++; + } + } + return str.substring( start ); + } + + // Case conversion + //-------------------------------------------------------------------------- + + /** + *

          Convert a String to upper case, null String + * returns null.

          + * + * @param str the String to uppercase + * @return the upper cased String + */ + public static String upperCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toUpperCase(); + } + + /** + *

          Convert a String to lower case, null String + * returns null.

          + * + * @param str the string to lowercase + * @return the lower cased String + */ + public static String lowerCase( String str ) + { + if ( str == null ) + { + return null; + } + return str.toLowerCase(); + } + + /** + *

          Uncapitalise a String.

          + * + *

          That is, convert the first character into lower-case. + * null is returned as null.

          + * + * @param str the String to uncapitalise + * @return uncapitalised String + */ + public static String uncapitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuffer( length ) + .append( Character.toLowerCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Capitalise a String.

          + * + *

          That is, convert the first character into title-case. + * null is returned as null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitalise( String str ) + { + if ( str == null ) + { + return null; + } + else + { + int length = str.length(); + if ( length == 0 ) + { + return ""; + } + else + { + return new StringBuilder( length ) + .append( Character.toTitleCase( str.charAt( 0 ) ) ) + .append( str, 1, length ) + .toString(); + } + } + } + + /** + *

          Swaps the case of String.

          + * + *

          Properly looks after making sure the start of words + * are Titlecase and not Uppercase.

          + * + *

          null is returned as null.

          + * + * @param str the String to swap the case of + * @return the modified String + */ + public static String swapCase( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + + boolean whitespace = false; + char ch; + char tmp; + + for ( int i = 0; i < sz; i++ ) + { + ch = str.charAt( i ); + if ( Character.isUpperCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isTitleCase( ch ) ) + { + tmp = Character.toLowerCase( ch ); + } + else if ( Character.isLowerCase( ch ) ) + { + if ( whitespace ) + { + tmp = Character.toTitleCase( ch ); + } + else + { + tmp = Character.toUpperCase( ch ); + } + } + else + { + tmp = ch; + } + buffer.append( tmp ); + whitespace = Character.isWhitespace( ch ); + } + return buffer.toString(); + } + + + /** + *

          Capitalise all the words in a String.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the String to capitalise + * @return capitalised String + */ + public static String capitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toTitleCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + /** + *

          Uncapitalise all the words in a string.

          + * + *

          Uses {@link Character#isWhitespace(char)} as a + * separator between words.

          + * + *

          null will return null.

          + * + * @param str the string to uncapitalise + * @return uncapitalised string + */ + public static String uncapitaliseAllWords( String str ) + { + if ( str == null ) + { + return null; + } + int sz = str.length(); + StringBuilder buffer = new StringBuilder( sz ); + boolean space = true; + for ( int i = 0; i < sz; i++ ) + { + char ch = str.charAt( i ); + if ( Character.isWhitespace( ch ) ) + { + buffer.append( ch ); + space = true; + } + else if ( space ) + { + buffer.append( Character.toLowerCase( ch ) ); + space = false; + } + else + { + buffer.append( ch ); + } + } + return buffer.toString(); + } + + // Nested extraction + //-------------------------------------------------------------------------- + + /** + *

          Get the String that is nested in between two instances of the + * same String.

          + * + *

          If str is null, will + * return null.

          + * + * @param str the String containing nested-string + * @param tag the String before and after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if tag is null + */ + public static String getNestedString( String str, @Nonnull String tag ) + { + return getNestedString( str, tag, tag ); + } + + /** + *

          Get the String that is nested in between two Strings.

          + * + * @param str the String containing nested-string + * @param open the String before nested-string + * @param close the String after nested-string + * @return the String that was nested, or null + * @throws NullPointerException if open or close is null + */ + public static String getNestedString( String str, @Nonnull String open, @Nonnull String close ) + { + if ( str == null ) + { + return null; + } + int start = str.indexOf( open ); + if ( start != -1 ) + { + int end = str.indexOf( close, start + open.length() ); + if ( end != -1 ) + { + return str.substring( start + open.length(), end ); + } + } + return null; + } + + /** + *

          How many times is the substring in the larger String.

          + * + *

          null returns 0.

          + * + * @param str the String to check + * @param sub the substring to count + * @return the number of occurrences, 0 if the String is null + * @throws NullPointerException if sub is null + */ + public static int countMatches( @Nullable String str, @Nonnull String sub ) + { + if ( sub.equals( "" ) ) + { + return 0; + } + if ( str == null ) + { + return 0; + } + int count = 0; + int idx = 0; + while ( ( idx = str.indexOf( sub, idx ) ) != -1 ) + { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //-------------------------------------------------------------------------- + + /** + *

          Checks if the String contains only Unicode letters.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, and is non-null + */ + public static boolean isAlpha( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetter( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only whitespace.

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains whitespace, and is non-null + */ + public static boolean isWhitespace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isWhitespace( str.charAt( i ) ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters and + * space (' ').

          + * + *

          null will return false. An + * empty String will return true.

          + * + * @param str the String to check + * @return true if only contains letters and space, + * and is non-null + */ + public static boolean isAlphaSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetter( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters or digits.

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters or digits, + * and is non-null + */ + public static boolean isAlphanumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isLetterOrDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode letters, digits + * or space (' ').

          + * + *

          null will return false. An empty + * String will return true.

          + * + * @param str the String to check + * @return true if only contains letters, digits or space, + * and is non-null + */ + public static boolean isAlphanumericSpace( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( ( !Character.isLetterOrDigit( str.charAt( i ) ) ) && ( str.charAt( i ) != ' ' ) ) + { + return false; + } + } + return true; + } + + /** + *

          Checks if the String contains only Unicode digits.

          + * + *

          null will return false. + * An empty String will return true.

          + * + * @param str the String to check + * @return true if only contains digits, and is non-null + */ + public static boolean isNumeric( String str ) + { + if ( str == null ) + { + return false; + } + int sz = str.length(); + for ( int i = 0; i < sz; i++ ) + { + if ( !Character.isDigit( str.charAt( i ) ) ) + { + return false; + } + } + return true; + } + + // Defaults + //-------------------------------------------------------------------------- + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, an empty + * String.

          + * + * @param obj the Object to check + * @return the passed in Object's toString, or blank if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj ) + { + return defaultString( obj, "" ); + } + + /** + *

          Returns either the passed in Object as a String, + * or, if the Object is null, a passed + * in default String.

          + * + * @param obj the Object to check + * @param defaultString the default String to return if str is + * null + * @return the passed in string, or the default if it was + * null + * @deprecated use {@code java.lang.Objects.toString()} + */ + @Deprecated + @Nonnull public static String defaultString( Object obj, @Nonnull String defaultString ) + { + return ( obj == null ) ? defaultString : obj.toString(); + } + + // Reversing + //-------------------------------------------------------------------------- + + /** + *

          Reverse a String.

          + * + *

          null String returns null.

          + * + * @param str the String to reverse + * @return the reversed String + */ + public static String reverse( String str ) + { + if ( str == null ) + { + return null; + } + return new StringBuffer( str ).reverse().toString(); + } + + /** + *

          Reverses a String that is delimited by a specific character.

          + * + *

          The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is '.').

          + * + * @param str the String to reverse + * @param delimiter the delimiter to use + * @return the reversed String + */ + @Nonnull public static String reverseDelimitedString( @Nonnull String str, String delimiter ) + { + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split( str, delimiter ); + reverseArray( strs ); + return join( strs, delimiter ); + } + + /** + *

          Reverses an array.

          + * + * @param array the array to reverse + */ + private static void reverseArray( @Nonnull String... array ) + { + int i = 0; + int j = array.length - 1; + String tmp; + + while ( j > i ) + { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // Abbreviating + //-------------------------------------------------------------------------- + + /** + * Turn "Now is the time for all good men" into "Now is the time for..." + *

          + * Specifically: + *

          + * If str is less than max characters long, return it. + * Else abbreviate it to (substring(str, 0, max-3) + "..."). + * If maxWidth is less than 3, throw an IllegalArgumentException. + * In no case will it return a string of length greater than maxWidth. + * + * @param s The string to be abbreviated. + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int maxWidth ) + { + return abbreviate( s, 0, maxWidth ); + } + + /** + * Turn "Now is the time for all good men" into "...is the time for..." + *

          + * Works like abbreviate(String, int), but allows you to specify a "left edge" + * offset. Note that this left edge is not necessarily going to be the leftmost + * character in the result, or the first + * character following the ellipses, but it will appear somewhere in the result. + * In no case will it return a string of length greater than maxWidth. + * + * @param s String to abbreviate. + * @param offset left edge of source string + * @param maxWidth maximum length of result string + * @return The abbreviated string. + */ + @Nonnull public static String abbreviate( @Nonnull String s, int offset, int maxWidth ) + { + if ( maxWidth < 4 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width is 4" ); + } + if ( s.length() <= maxWidth ) + { + return s; + } + if ( offset > s.length() ) + { + offset = s.length(); + } + if ( ( s.length() - offset ) < ( maxWidth - 3 ) ) + { + offset = s.length() - ( maxWidth - 3 ); + } + if ( offset <= 4 ) + { + return s.substring( 0, maxWidth - 3 ) + "..."; + } + if ( maxWidth < 7 ) + { + throw new IllegalArgumentException( "Minimum abbreviation width with offset is 7" ); + } + if ( ( offset + ( maxWidth - 3 ) ) < s.length() ) + { + return "..." + abbreviate( s.substring( offset ), maxWidth - 3 ); + } + return "..." + s.substring( s.length() - ( maxWidth - 3 ) ); + } + + // Difference + //-------------------------------------------------------------------------- + + /** + * Compare two strings, and return the portion where they differ. + * (More precisely, return the remainder of the second string, + * starting from where it's different from the first.) + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> "robot" + * + * @param s1 The first string. + * @param s2 The second string. + * @return the portion of s2 where it differs from s1; returns the empty string ("") if they are equal + */ + public static String difference( @Nonnull String s1, @Nonnull String s2 ) + { + int at = differenceAt( s1, s2 ); + if ( at == -1 ) + { + return ""; + } + return s2.substring( at ); + } + + /** + * Compare two strings, and return the index at which the strings begin to differ. + *

          + * E.g. strdiff("i am a machine", "i am a robot") -> 7 + *

          + * + * @param s1 The first string. + * @param s2 The second string. + * @return the index where s2 and s1 begin to differ; -1 if they are equal + */ + public static int differenceAt( @Nonnull String s1, @Nonnull String s2 ) + { + int i; + for ( i = 0; ( i < s1.length() ) && ( i < s2.length() ); ++i ) + { + if ( s1.charAt( i ) != s2.charAt( i ) ) + { + break; + } + } + if ( ( i < s2.length() ) || ( i < s1.length() ) ) + { + return i; + } + return -1; + } + + /** + * Fill all 'variables' in the given text with the values from the map. + * Any text looking like '${key}' will get replaced by the value stored + * in the namespace map under the 'key'. + * + * @param text The text where replacements will be searched for. + * @param namespace The namespace which contains the replacements. + * @return the interpolated text. + */ + public static String interpolate( String text, @Nonnull Map namespace ) + { + for ( Map.Entry entry : namespace.entrySet() ) + { + String key = entry.getKey().toString(); + + Object obj = entry.getValue(); + + if ( obj == null ) + { + throw new NullPointerException( "The value of the key '" + key + "' is null." ); + } + + String value = obj.toString(); + + text = replace( text, "${" + key + "}", value ); + + if ( !key.contains( " " ) ) + { + text = replace( text, "$" + key, value ); + } + } + return text; + } + + /** + * This is basically the inverse of {@link #addAndDeHump(String)}. + * It will remove the 'replaceThis' parameter and uppercase the next + * character afterwards. + *
          +     * removeAndHump( "this-is-it", %quot;-" );
          +     * 
          + * will become 'ThisIsIt'. + * + * @param data The data. + * @param replaceThis The things which should be replaced. + * @return humped String + */ + @Nonnull public static String removeAndHump( @Nonnull String data, @Nonnull String replaceThis ) + { + String temp; + + StringBuilder out = new StringBuilder(); + + temp = data; + + StringTokenizer st = new StringTokenizer( temp, replaceThis ); + + while ( st.hasMoreTokens() ) + { + String element = st.nextToken(); + + out.append( capitalizeFirstLetter( element ) ); + } + + return out.toString(); + } + + /** + * Converts the first character of the given String to uppercase. + * This method does not trim spaces! + * + * @param data the String to get capitalized + * @return data string with the first character transformed to uppercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String capitalizeFirstLetter( @Nonnull String data ) + { + char firstChar = data.charAt( 0 ); + char titleCase = Character.toTitleCase( firstChar ); + if ( firstChar == titleCase ) + { + return data; + } + StringBuilder result = new StringBuilder( data.length() ); + result.append( titleCase ); + result.append( data, 1, data.length() ); + return result.toString(); + } + + /** + * Converts the first character of the given String to lowercase. + * This method does not trim spaces! + *

          + * + * @param data the String to get it's first character lower-cased. + * @return data string with the first character transformed to lowercase + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if data is empty + */ + @Nonnull public static String lowercaseFirstLetter( @Nonnull String data ) + { + char firstLetter = Character.toLowerCase( data.substring( 0, 1 ).charAt( 0 ) ); + + String restLetters = data.substring( 1 ); + + return firstLetter + restLetters; + } + + /** + * Take the input string and un-camel-case it. + *

          + * 'ThisIsIt' will become 'this-is-it'. + * + * @param view the view + * @return deHumped String + */ + @Nonnull public static String addAndDeHump( @Nonnull String view ) + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < view.length(); i++ ) + { + if ( ( i != 0 ) && Character.isUpperCase( view.charAt( i ) ) ) + { + sb.append( '-' ); + } + + sb.append( view.charAt( i ) ); + } + + return sb.toString().trim().toLowerCase( Locale.ENGLISH ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + *
          +     * StringUtils.quoteAndEscape(null, *)    = null
          +     * StringUtils.quoteAndEscape("", *)      = ""
          +     * StringUtils.quoteAndEscape("abc", '"') = abc
          +     * StringUtils.quoteAndEscape("a\"bc", '"') = "a\"bc"
          +     * StringUtils.quoteAndEscape("a\"bc", '\'') = 'a\"bc'
          +     * 
          + * + * @param source The source. + * @param quoteChar The quote character. + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, new char[]{ ' ' }, '\\', false ); + } + + /** + *

          Quote and escape a String with the given character, handling null.

          + * + * @param source the source + * @param quoteChar the quote character + * @param quotingTriggers the quoting trigger + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull char[] quotingTriggers ) + { + return quoteAndEscape( source, quoteChar, new char[]{ quoteChar }, quotingTriggers, '\\', false ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars the escaped characters + * @param escapeChar the escape character + * @param force true/false + * @return the String quoted and escaped + * @see #quoteAndEscape(String, char, char[], char[], char, boolean) + * + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, + @Nonnull final char[] escapedChars, char escapeChar, boolean force ) + { + return quoteAndEscape( source, quoteChar, escapedChars, new char[]{ ' ' }, escapeChar, force ); + } + + /** + * @param source the source + * @param quoteChar the quote character + * @param escapedChars set of characters to escape + * @param quotingTriggers the quoting trigger + * @param escapeChar prefix for escaping a character + * @param force true/false + * @return the String quoted and escaped + */ + public static String quoteAndEscape( @Nullable String source, char quoteChar, @Nonnull final char[] escapedChars, + @Nonnull final char[] quotingTriggers, char escapeChar, boolean force ) + { + if ( source == null ) + { + return null; + } + + if ( !force && source.startsWith( Character.toString( quoteChar ) ) && source.endsWith( + Character.toString( quoteChar ) ) ) + { + return source; + } + + String escaped = escape( source, escapedChars, escapeChar ); + + boolean quote = false; + if ( force ) + { + quote = true; + } + else if ( !escaped.equals( source ) ) + { + quote = true; + } + else + { + for ( char quotingTrigger : quotingTriggers ) + { + if ( escaped.indexOf( quotingTrigger ) > -1 ) + { + quote = true; + break; + } + } + } + + if ( quote ) + { + return quoteChar + escaped + quoteChar; + } + + return escaped; + } + + /** + * @param source the source + * @param escapedChars set of characters to escape + * @param escapeChar prefix for escaping a character + * @return the String escaped + */ + public static String escape( @Nullable String source, @Nonnull final char[] escapedChars, char escapeChar ) + { + if ( source == null ) + { + return null; + } + + char[] eqc = new char[escapedChars.length]; + System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); + Arrays.sort( eqc ); + + StringBuilder buffer = new StringBuilder( source.length() ); + + for ( int i = 0; i < source.length(); i++ ) + { + final char c = source.charAt( i ); + int result = Arrays.binarySearch( eqc, c ); + + if ( result > -1 ) + { + buffer.append( escapeChar ); + } + buffer.append( c ); + } + + return buffer.toString(); + } + + /** + * Remove all duplicate whitespace characters and replace line terminators with a single space. + * + * @param s a not null String + * @return a string with unique whitespace + * + */ + @Nonnull public static String removeDuplicateWhitespace( @Nonnull String s ) + { + StringBuilder result = new StringBuilder(); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for ( int i = 0; i < length; i++ ) + { + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if ( !( isPreviousWhiteSpace && thisCharWhiteSpace ) ) + { + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @return a String that contains only System line separators + * @see #unifyLineSeparators(String, String) + * + */ + public static String unifyLineSeparators( @Nullable String s ) + { + return unifyLineSeparators( s, System.getProperty( "line.separator" ) ); + } + + /** + * Parses the given String and replaces all occurrences of + * '\n', '\r' and '\r\n' with the system line separator. + * + * @param s a not null String + * @param ls the wanted line separator ("\n" on UNIX), if null using the System line separator. + * @return a String that contains only System line separators + * @throws IllegalArgumentException if ls is not '\n', '\r' and '\r\n' characters. + * + */ + public static String unifyLineSeparators( @Nullable String s, @Nullable String ls ) + { + if ( s == null ) + { + return null; + } + + if ( ls == null ) + { + ls = System.getProperty( "line.separator" ); + } + + if ( !( ls.equals( "\n" ) || ls.equals( "\r" ) || ls.equals( "\r\n" ) ) ) + { + throw new IllegalArgumentException( "Requested line separator is invalid." ); + } + + int length = s.length(); + + StringBuilder buffer = new StringBuilder( length ); + for ( int i = 0; i < length; i++ ) + { + if ( s.charAt( i ) == '\r' ) + { + if ( ( i + 1 ) < length && s.charAt( i + 1 ) == '\n' ) + { + i++; + } + + buffer.append( ls ); + } + else if ( s.charAt( i ) == '\n' ) + { + buffer.append( ls ); + } + else + { + buffer.append( s.charAt( i ) ); + } + } + + return buffer.toString(); + } + + /** + *

          Checks if String contains a search character, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null or empty ("") String will return false.

          + * + *
          +     * StringUtils.contains(null, *)    = false
          +     * StringUtils.contains("", *)      = false
          +     * StringUtils.contains("abc", 'a') = true
          +     * StringUtils.contains("abc", 'z') = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchChar the character to find + * @return true if the String contains the search character, + * false if not or null string input + * + */ + public static boolean contains( @Nullable String str, char searchChar ) + { + return !isEmpty( str ) && str.indexOf( searchChar ) >= 0; + } + + /** + *

          Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(int)}.

          + * + *

          A null String will return false.

          + * + *
          +     * StringUtils.contains(null, *)     = false
          +     * StringUtils.contains(*, null)     = false
          +     * StringUtils.contains("", "")      = true
          +     * StringUtils.contains("abc", "")   = true
          +     * StringUtils.contains("abc", "a")  = true
          +     * StringUtils.contains("abc", "z")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String, + * false if not or null string input + */ + public static boolean contains( @Nullable String str, @Nullable String searchStr ) + { + return !( str == null || searchStr == null ) && str.contains( searchStr ); + } + + /** + *

          Checks if String ends with a search String, handling null.

          + *

          + *

          A null String will return false.

          + * + *
          +     * StringUtils.endsWithIgnoreCase(null, *)     = false
          +     * StringUtils.endsWithIgnoreCase(*, null)     = false
          +     * StringUtils.endsWithIgnoreCase("", "")      = true
          +     * StringUtils.endsWithIgnoreCase("abc", "")   = true
          +     * StringUtils.endsWithIgnoreCase("abc", "C")  = true
          +     * StringUtils.endsWithIgnoreCase("abc", "a")  = false
          +     * 
          + * + * @param str the String to check, may be null + * @param searchStr the String to find at end, may be null + * @return true if the String ends with the search String, + * false if not or null string input + * + */ + public static boolean endsWithIgnoreCase( @Nullable String str, @Nullable String searchStr ) + { + if ( str == null || searchStr == null ) + { + // for consistency with contains + return false; + } + + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } +} diff --git a/Java/maven-shared-utils-StringUtils_96/metadata.json b/Java/maven-shared-utils-StringUtils_96/metadata.json new file mode 100644 index 000000000..89912ec19 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_96/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-StringUtils_96", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 106, + "npe_method": "trim", + "deref_field": "str", + "npe_class": "StringUtils", + "repo": "maven-shared-utils", + "bug_id": "StringUtils_96" + } +} diff --git a/Java/maven-shared-utils-StringUtils_96/npe.json b/Java/maven-shared-utils-StringUtils_96/npe.json new file mode 100644 index 000000000..721944623 --- /dev/null +++ b/Java/maven-shared-utils-StringUtils_96/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/StringUtils.java", + "line": 106, + "npe_method": "trim", + "deref_field": "str", + "npe_class": "StringUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Style_117/Dockerfile b/Java/maven-shared-utils-Style_117/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Style_117/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Style_117/buggy.java b/Java/maven-shared-utils-Style_117/buggy.java new file mode 100644 index 000000000..21e02a055 --- /dev/null +++ b/Java/maven-shared-utils-Style_117/buggy.java @@ -0,0 +1,173 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.Ansi.Color; + +import java.util.Locale; + +/** + * Configurable message styles. + * @since 3.1.0 + */ +enum Style +{ + + DEBUG( "bold,cyan" ), + INFO( "bold,blue" ), + WARNING( "bold,yellow" ), + ERROR( "bold,red" ), + SUCCESS( "bold,green" ), + FAILURE( "bold,red" ), + STRONG( "bold" ), + MOJO( "green" ), + PROJECT( "cyan" ); + + private final boolean bold; + + private final boolean bright; + + private final Color color; + + private final boolean bgBright; + + private final Color bgColor; + + Style( String defaultValue ) + { + boolean currentBold = false; + boolean currentBright = false; + Color currentColor = null; + boolean currentBgBright = false; + Color currentBgColor = null; + + String value = System.getProperty( "style." + name().toLowerCase( Locale.ENGLISH ), + defaultValue ).toLowerCase( Locale.ENGLISH ); + + for ( String token : value.split( "," ) ) + { + if ( "bold".equals( token ) ) + { + currentBold = true; + } + else if ( token.startsWith( "bg" ) ) + { + token = token.substring( 2 ); + if ( token.startsWith( "bright" ) ) + { + currentBgBright = true; + token = token.substring( 6 ); + } + currentBgColor = toColor( token ); + } + else + { + if ( token.startsWith( "bright" ) ) + { + currentBright = true; + token = token.substring( 6 ); + } + currentColor = toColor( token ); + } + } + + this.bold = currentBold; + this.bright = currentBright; + this.color = currentColor; + this.bgBright = currentBgBright; + this.bgColor = currentBgColor; + } + + private static Color toColor( String token ) + { + for ( Color color : Color.values() ) + { + if ( color.toString().equalsIgnoreCase( token ) ) + { + return color; + } + } + return null; + } + +org.fusesource.jansi.Ansi apply(org.fusesource.jansi.Ansi ansi) { + if (bold) { + ansi.bold(); + } + { + if (bright) { + ansi.fgBright(/* NPEX_NULL_EXP */ + color); + } else { + ansi.fg(color); + } + } + if (bgColor != null) { + if (bgBright) { + ansi.bgBright(bgColor); + } else { + ansi.bg(bgColor); + } + } + return ansi; +} + + @Override + public String toString() + { + if ( !bold && color == null && bgColor == null ) + { + return name(); + } + StringBuilder sb = new StringBuilder( name() + '=' ); + if ( bold ) + { + sb.append( "bold" ); + } + if ( color != null ) + { + if ( sb.length() > 0 ) + { + sb.append( ',' ); + } + if ( bright ) + { + sb.append( "bright" ); + } + sb.append( color.name() ); + } + if ( bgColor != null ) + { + if ( sb.length() > 0 ) + { + sb.append( ',' ); + } + sb.append( "bg" ); + if ( bgBright ) + { + sb.append( "bright" ); + } + sb.append( bgColor.name() ); + } + return sb.toString(); + } + +} diff --git a/Java/maven-shared-utils-Style_117/metadata.json b/Java/maven-shared-utils-Style_117/metadata.json new file mode 100644 index 000000000..8362d3d5e --- /dev/null +++ b/Java/maven-shared-utils-Style_117/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Style_117", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/logging/Style.java", + "line": 118, + "npe_method": "apply", + "deref_field": "color", + "npe_class": "Style", + "repo": "maven-shared-utils", + "bug_id": "Style_117" + } +} diff --git a/Java/maven-shared-utils-Style_117/npe.json b/Java/maven-shared-utils-Style_117/npe.json new file mode 100644 index 000000000..a8296d301 --- /dev/null +++ b/Java/maven-shared-utils-Style_117/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/logging/Style.java", + "line": 118, + "npe_method": "apply", + "deref_field": "color", + "npe_class": "Style" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Style_128/Dockerfile b/Java/maven-shared-utils-Style_128/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Style_128/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Style_128/buggy.java b/Java/maven-shared-utils-Style_128/buggy.java new file mode 100644 index 000000000..9b33c43cd --- /dev/null +++ b/Java/maven-shared-utils-Style_128/buggy.java @@ -0,0 +1,173 @@ +package org.apache.maven.shared.utils.logging; + +/* + * 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. + */ + +import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.Ansi.Color; + +import java.util.Locale; + +/** + * Configurable message styles. + * @since 3.1.0 + */ +enum Style +{ + + DEBUG( "bold,cyan" ), + INFO( "bold,blue" ), + WARNING( "bold,yellow" ), + ERROR( "bold,red" ), + SUCCESS( "bold,green" ), + FAILURE( "bold,red" ), + STRONG( "bold" ), + MOJO( "green" ), + PROJECT( "cyan" ); + + private final boolean bold; + + private final boolean bright; + + private final Color color; + + private final boolean bgBright; + + private final Color bgColor; + + Style( String defaultValue ) + { + boolean currentBold = false; + boolean currentBright = false; + Color currentColor = null; + boolean currentBgBright = false; + Color currentBgColor = null; + + String value = System.getProperty( "style." + name().toLowerCase( Locale.ENGLISH ), + defaultValue ).toLowerCase( Locale.ENGLISH ); + + for ( String token : value.split( "," ) ) + { + if ( "bold".equals( token ) ) + { + currentBold = true; + } + else if ( token.startsWith( "bg" ) ) + { + token = token.substring( 2 ); + if ( token.startsWith( "bright" ) ) + { + currentBgBright = true; + token = token.substring( 6 ); + } + currentBgColor = toColor( token ); + } + else + { + if ( token.startsWith( "bright" ) ) + { + currentBright = true; + token = token.substring( 6 ); + } + currentColor = toColor( token ); + } + } + + this.bold = currentBold; + this.bright = currentBright; + this.color = currentColor; + this.bgBright = currentBgBright; + this.bgColor = currentBgColor; + } + + private static Color toColor( String token ) + { + for ( Color color : Color.values() ) + { + if ( color.toString().equalsIgnoreCase( token ) ) + { + return color; + } + } + return null; + } + +org.fusesource.jansi.Ansi apply(org.fusesource.jansi.Ansi ansi) { + if (bold) { + ansi.bold(); + } + if (color != null) { + if (bright) { + ansi.fgBright(color); + } else { + ansi.fg(color); + } + } + { + if (bgBright) { + ansi.bgBright(/* NPEX_NULL_EXP */ + bgColor); + } else { + ansi.bg(bgColor); + } + } + return ansi; +} + + @Override + public String toString() + { + if ( !bold && color == null && bgColor == null ) + { + return name(); + } + StringBuilder sb = new StringBuilder( name() + '=' ); + if ( bold ) + { + sb.append( "bold" ); + } + if ( color != null ) + { + if ( sb.length() > 0 ) + { + sb.append( ',' ); + } + if ( bright ) + { + sb.append( "bright" ); + } + sb.append( color.name() ); + } + if ( bgColor != null ) + { + if ( sb.length() > 0 ) + { + sb.append( ',' ); + } + sb.append( "bg" ); + if ( bgBright ) + { + sb.append( "bright" ); + } + sb.append( bgColor.name() ); + } + return sb.toString(); + } + +} diff --git a/Java/maven-shared-utils-Style_128/metadata.json b/Java/maven-shared-utils-Style_128/metadata.json new file mode 100644 index 000000000..7e6a9a262 --- /dev/null +++ b/Java/maven-shared-utils-Style_128/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Style_128", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/logging/Style.java", + "line": 125, + "npe_method": "apply", + "deref_field": "bgColor", + "npe_class": "Style", + "repo": "maven-shared-utils", + "bug_id": "Style_128" + } +} diff --git a/Java/maven-shared-utils-Style_128/npe.json b/Java/maven-shared-utils-Style_128/npe.json new file mode 100644 index 000000000..d6da638b6 --- /dev/null +++ b/Java/maven-shared-utils-Style_128/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/logging/Style.java", + "line": 125, + "npe_method": "apply", + "deref_field": "bgColor", + "npe_class": "Style" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-XmlStreamReaderTest_63/Dockerfile b/Java/maven-shared-utils-XmlStreamReaderTest_63/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-XmlStreamReaderTest_63/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-XmlStreamReaderTest_63/buggy.java b/Java/maven-shared-utils-XmlStreamReaderTest_63/buggy.java new file mode 100644 index 000000000..cbee0b556 --- /dev/null +++ b/Java/maven-shared-utils-XmlStreamReaderTest_63/buggy.java @@ -0,0 +1,207 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.SequenceInputStream; + + +import junit.framework.ComparisonFailure; +import junit.framework.TestCase; + +import org.apache.commons.io.IOUtils; +import org.apache.maven.shared.utils.xml.XmlStreamReader; + +/** + * + * @author Hervé Boutemy + */ +public class XmlStreamReaderTest + extends TestCase +{ + /** french */ + private static final String TEXT_LATIN1 = "eacute: \u00E9"; + /** greek */ + private static final String TEXT_LATIN7 = "alpha: \u03B1"; + /** euro support */ + private static final String TEXT_LATIN15 = "euro: \u20AC"; + /** japanese */ + private static final String TEXT_EUC_JP = "hiragana A: \u3042"; + /** Unicode: support everything */ + private static final String TEXT_UNICODE = + TEXT_LATIN1 + ", " + + TEXT_LATIN7 + ", " + + TEXT_LATIN15 + ", " + + TEXT_EUC_JP; + /** see http://unicode.org/faq/utf_bom.html#BOM */ + private static final byte[] BOM_UTF8 = { (byte)0xEF, (byte)0xBB, (byte)0xBF }; + private static final byte[] BOM_UTF16BE = { (byte)0xFE, (byte)0xFF }; + private static final byte[] BOM_UTF16LE = { (byte)0xFF, (byte)0xFE }; + +private static java.lang.String createXmlContent(java.lang.String text, java.lang.String encoding) { + java.lang.String xmlDecl = ""; + { + xmlDecl = (""; + } + return ((xmlDecl + "\n") + text) + ""; +} + + private static void checkXmlContent( String xml, String encoding ) throws IOException + { + checkXmlContent( xml, encoding, null ); + } + + private static void checkXmlContent( String xml, String encoding, byte[] bom ) throws IOException + { + byte[] xmlContent = xml.getBytes( encoding ); + InputStream in = new ByteArrayInputStream( xmlContent ); + + if ( bom != null ) + { + in = new SequenceInputStream( new ByteArrayInputStream( bom ), in ); + } + + XmlStreamReader reader = new XmlStreamReader( in ); + assertEquals( encoding, reader.getEncoding() ); + String result = IOUtils.toString( reader ); + assertEquals( xml, result ); + } + + private static void checkXmlStreamReader( String text, String encoding, String effectiveEncoding ) + throws IOException + { + checkXmlStreamReader( text, encoding, effectiveEncoding, null ); + } + + private static void checkXmlStreamReader( String text, String encoding ) throws IOException + { + checkXmlStreamReader( text, encoding, encoding, null ); + } + + private static void checkXmlStreamReader( String text, String encoding, byte[] bom ) throws IOException + { + checkXmlStreamReader( text, encoding, encoding, bom ); + } + + private static void checkXmlStreamReader( String text, String encoding, String effectiveEncoding, byte[] bom ) + throws IOException + { + String xml = createXmlContent( text, encoding ); + checkXmlContent( xml, effectiveEncoding, bom ); + } + + public void testNoXmlHeader() throws IOException + { + String xml = "text with no XML header"; + checkXmlContent( xml, "UTF-8" ); + checkXmlContent( xml, "UTF-8", BOM_UTF8 ); + } + + public void testDefaultEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, null, "UTF-8" ); + checkXmlStreamReader( TEXT_UNICODE, null, "UTF-8", BOM_UTF8 ); + } + + public void testUTF8Encoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-8" ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-8", BOM_UTF8 ); + } + + public void testUTF16Encoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16BE", null ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16LE", BOM_UTF16LE ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16BE", BOM_UTF16BE ); + } + + public void testUTF16BEEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16BE" ); + } + + public void testUTF16LEEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16LE" ); + } + + public void testLatin1Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN1, "ISO-8859-1" ); + } + + public void testLatin7Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN7, "ISO-8859-7" ); + } + + public void testLatin15Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN15, "ISO-8859-15" ); + } + + public void testEUC_JPEncoding() throws IOException + { + checkXmlStreamReader( TEXT_EUC_JP, "EUC-JP" ); + } + + public void testEBCDICEncoding() throws IOException + { + checkXmlStreamReader( "simple text in EBCDIC", "CP1047" ); + } + + public void testInappropriateEncoding() throws IOException + { + try + { + checkXmlStreamReader( TEXT_UNICODE, "ISO-8859-2" ); + fail( "Check should have failed, since some characters are not available in the specified encoding" ); + } + catch ( ComparisonFailure cf ) + { + // expected failure, since the encoding does not contain some characters + } + } + + public void testEncodingAttribute() throws IOException + { + String xml = ""; + checkXmlContent( xml, "US-ASCII" ); + + xml = ""; + checkXmlContent( xml, "US-ASCII" ); + + xml = ""; + checkXmlContent( xml, "UTF-8" ); + + xml = "\n"; + checkXmlContent( xml, "US-ASCII" ); + + xml = "\n"; + checkXmlContent( xml, "UTF-8" ); + + xml = ""; + checkXmlContent( xml, "UTF-8" ); + } +} diff --git a/Java/maven-shared-utils-XmlStreamReaderTest_63/metadata.json b/Java/maven-shared-utils-XmlStreamReaderTest_63/metadata.json new file mode 100644 index 000000000..95f42c0eb --- /dev/null +++ b/Java/maven-shared-utils-XmlStreamReaderTest_63/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-XmlStreamReaderTest_63", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/shared/utils/XmlStreamReaderTest.java", + "line": 64, + "npe_method": "createXmlContent", + "deref_field": "encoding", + "npe_class": "XmlStreamReaderTest", + "repo": "maven-shared-utils", + "bug_id": "XmlStreamReaderTest_63" + } +} diff --git a/Java/maven-shared-utils-XmlStreamReaderTest_63/npe.json b/Java/maven-shared-utils-XmlStreamReaderTest_63/npe.json new file mode 100644 index 000000000..324566ab8 --- /dev/null +++ b/Java/maven-shared-utils-XmlStreamReaderTest_63/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/shared/utils/XmlStreamReaderTest.java", + "line": 64, + "npe_method": "createXmlContent", + "deref_field": "encoding", + "npe_class": "XmlStreamReaderTest" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-XmlStreamReaderTest_80/Dockerfile b/Java/maven-shared-utils-XmlStreamReaderTest_80/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-XmlStreamReaderTest_80/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-XmlStreamReaderTest_80/buggy.java b/Java/maven-shared-utils-XmlStreamReaderTest_80/buggy.java new file mode 100644 index 000000000..3bf64eb8c --- /dev/null +++ b/Java/maven-shared-utils-XmlStreamReaderTest_80/buggy.java @@ -0,0 +1,205 @@ +package org.apache.maven.shared.utils; + +/* + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.SequenceInputStream; + + +import junit.framework.ComparisonFailure; +import junit.framework.TestCase; + +import org.apache.commons.io.IOUtils; +import org.apache.maven.shared.utils.xml.XmlStreamReader; + +/** + * + * @author Hervé Boutemy + */ +public class XmlStreamReaderTest + extends TestCase +{ + /** french */ + private static final String TEXT_LATIN1 = "eacute: \u00E9"; + /** greek */ + private static final String TEXT_LATIN7 = "alpha: \u03B1"; + /** euro support */ + private static final String TEXT_LATIN15 = "euro: \u20AC"; + /** japanese */ + private static final String TEXT_EUC_JP = "hiragana A: \u3042"; + /** Unicode: support everything */ + private static final String TEXT_UNICODE = + TEXT_LATIN1 + ", " + + TEXT_LATIN7 + ", " + + TEXT_LATIN15 + ", " + + TEXT_EUC_JP; + /** see http://unicode.org/faq/utf_bom.html#BOM */ + private static final byte[] BOM_UTF8 = { (byte)0xEF, (byte)0xBB, (byte)0xBF }; + private static final byte[] BOM_UTF16BE = { (byte)0xFE, (byte)0xFF }; + private static final byte[] BOM_UTF16LE = { (byte)0xFF, (byte)0xFE }; + + private static String createXmlContent( String text, String encoding ) + { + String xmlDecl = ""; + if ( encoding != null ) + { + xmlDecl = ""; + } + return xmlDecl + "\n" + text + ""; + } + + private static void checkXmlContent( String xml, String encoding ) throws IOException + { + checkXmlContent( xml, encoding, null ); + } + +private static void checkXmlContent(java.lang.String xml, java.lang.String encoding, byte[] bom) throws java.io.IOException { + byte[] xmlContent = xml.getBytes(encoding); + java.io.InputStream in = new java.io.ByteArrayInputStream(xmlContent); + { + in = new java.io.SequenceInputStream(new java.io.ByteArrayInputStream(/* NPEX_NULL_EXP */ + bom), in); + } + org.apache.maven.shared.utils.xml.XmlStreamReader reader = new org.apache.maven.shared.utils.xml.XmlStreamReader(in); + junit.framework.TestCase.assertEquals(encoding, reader.getEncoding()); + java.lang.String result = org.apache.commons.io.IOUtils.toString(reader); + junit.framework.TestCase.assertEquals(xml, result); +} + + private static void checkXmlStreamReader( String text, String encoding, String effectiveEncoding ) + throws IOException + { + checkXmlStreamReader( text, encoding, effectiveEncoding, null ); + } + + private static void checkXmlStreamReader( String text, String encoding ) throws IOException + { + checkXmlStreamReader( text, encoding, encoding, null ); + } + + private static void checkXmlStreamReader( String text, String encoding, byte[] bom ) throws IOException + { + checkXmlStreamReader( text, encoding, encoding, bom ); + } + + private static void checkXmlStreamReader( String text, String encoding, String effectiveEncoding, byte[] bom ) + throws IOException + { + String xml = createXmlContent( text, encoding ); + checkXmlContent( xml, effectiveEncoding, bom ); + } + + public void testNoXmlHeader() throws IOException + { + String xml = "text with no XML header"; + checkXmlContent( xml, "UTF-8" ); + checkXmlContent( xml, "UTF-8", BOM_UTF8 ); + } + + public void testDefaultEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, null, "UTF-8" ); + checkXmlStreamReader( TEXT_UNICODE, null, "UTF-8", BOM_UTF8 ); + } + + public void testUTF8Encoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-8" ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-8", BOM_UTF8 ); + } + + public void testUTF16Encoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16BE", null ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16LE", BOM_UTF16LE ); + checkXmlStreamReader( TEXT_UNICODE, "UTF-16", "UTF-16BE", BOM_UTF16BE ); + } + + public void testUTF16BEEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16BE" ); + } + + public void testUTF16LEEncoding() throws IOException + { + checkXmlStreamReader( TEXT_UNICODE, "UTF-16LE" ); + } + + public void testLatin1Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN1, "ISO-8859-1" ); + } + + public void testLatin7Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN7, "ISO-8859-7" ); + } + + public void testLatin15Encoding() throws IOException + { + checkXmlStreamReader( TEXT_LATIN15, "ISO-8859-15" ); + } + + public void testEUC_JPEncoding() throws IOException + { + checkXmlStreamReader( TEXT_EUC_JP, "EUC-JP" ); + } + + public void testEBCDICEncoding() throws IOException + { + checkXmlStreamReader( "simple text in EBCDIC", "CP1047" ); + } + + public void testInappropriateEncoding() throws IOException + { + try + { + checkXmlStreamReader( TEXT_UNICODE, "ISO-8859-2" ); + fail( "Check should have failed, since some characters are not available in the specified encoding" ); + } + catch ( ComparisonFailure cf ) + { + // expected failure, since the encoding does not contain some characters + } + } + + public void testEncodingAttribute() throws IOException + { + String xml = ""; + checkXmlContent( xml, "US-ASCII" ); + + xml = ""; + checkXmlContent( xml, "US-ASCII" ); + + xml = ""; + checkXmlContent( xml, "UTF-8" ); + + xml = "\n"; + checkXmlContent( xml, "US-ASCII" ); + + xml = "\n"; + checkXmlContent( xml, "UTF-8" ); + + xml = ""; + checkXmlContent( xml, "UTF-8" ); + } +} diff --git a/Java/maven-shared-utils-XmlStreamReaderTest_80/metadata.json b/Java/maven-shared-utils-XmlStreamReaderTest_80/metadata.json new file mode 100644 index 000000000..f9548f14e --- /dev/null +++ b/Java/maven-shared-utils-XmlStreamReaderTest_80/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-XmlStreamReaderTest_80", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/shared/utils/XmlStreamReaderTest.java", + "line": 80, + "npe_method": "checkXmlContent", + "deref_field": "bom", + "npe_class": "XmlStreamReaderTest", + "repo": "maven-shared-utils", + "bug_id": "XmlStreamReaderTest_80" + } +} diff --git a/Java/maven-shared-utils-XmlStreamReaderTest_80/npe.json b/Java/maven-shared-utils-XmlStreamReaderTest_80/npe.json new file mode 100644 index 000000000..bb4df92d5 --- /dev/null +++ b/Java/maven-shared-utils-XmlStreamReaderTest_80/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/shared/utils/XmlStreamReaderTest.java", + "line": 80, + "npe_method": "checkXmlContent", + "deref_field": "bom", + "npe_class": "XmlStreamReaderTest" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Xpp3DomUtils_141/Dockerfile b/Java/maven-shared-utils-Xpp3DomUtils_141/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomUtils_141/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Xpp3DomUtils_141/buggy.java b/Java/maven-shared-utils-Xpp3DomUtils_141/buggy.java new file mode 100644 index 000000000..ca8ce6125 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomUtils_141/buggy.java @@ -0,0 +1,162 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * + */ +public class Xpp3DomUtils +{ + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @param childMergeOverride true/false. + * @return Merged dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return dominant != null ? merge( dominant, recessive, childMergeOverride ) : recessive; + } + + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @return Merged dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return dominant != null ? merge( dominant, recessive, null ) : recessive; + } + + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @param childMergeOverride true/false. + * @return Merged dom. + */ + public static Xpp3Dom merge( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + if ( recessive == null || isCombineSelfOverride( dominant ) ) + { + return dominant; + } + + if ( isEmpty( dominant.getValue() ) ) + { + dominant.setValue( recessive.getValue() ); + } + + for ( String attr : recessive.getAttributeNames() ) + { + if ( isEmpty( dominant.getAttribute( attr ) ) ) + { + dominant.setAttribute( attr, recessive.getAttribute( attr ) ); + } + } + + if ( recessive.getChildCount() > 0 ) + { + boolean mergeChildren = isMergeChildren( dominant, childMergeOverride ); + + if ( mergeChildren ) + { + Map> commonChildren = getCommonChildren( dominant, recessive ); + for ( Xpp3Dom recessiveChild : recessive ) + { + Iterator it = commonChildren.get( recessiveChild.getName() ); + if ( it == null ) + { + dominant.addChild( new Xpp3Dom( recessiveChild ) ); + } + else if ( it.hasNext() ) + { + Xpp3Dom dominantChild = it.next(); + merge( dominantChild, recessiveChild, childMergeOverride ); + } + } + } + else + { + Xpp3Dom[] dominantChildren = dominant.getChildren(); + dominant.childList.clear(); + for ( Xpp3Dom child : recessive ) + { + dominant.addChild( new Xpp3Dom( child ) ); + } + + for ( Xpp3Dom aDominantChildren : dominantChildren ) + { + dominant.addChild( aDominantChildren ); + } + } + } + return dominant; + } + + private static Map> getCommonChildren( Xpp3Dom dominant, Xpp3Dom recessive ) + { + Map> commonChildren = new HashMap>(); + + for ( String childName : recessive.childMap.keySet() ) + { + List dominantChildren = dominant.getChildrenList( childName ); + if ( dominantChildren.size() > 0 ) + { + commonChildren.put( childName, dominantChildren.iterator() ); + } + } + return commonChildren; + } + + private static boolean isCombineSelfOverride( Xpp3Dom xpp3Dom ) + { + String selfMergeMode = xpp3Dom.getAttribute( Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE ); + return Xpp3Dom.SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ); + } + +private static boolean isMergeChildren(org.apache.maven.shared.utils.xml.Xpp3Dom dominant, java.lang.Boolean override) { + return /* NPEX_NULL_EXP */ + override; +} + + private static boolean isMergeChildren( Xpp3Dom dominant ) + { + return Xpp3Dom.CHILDREN_COMBINATION_APPEND.equals( + dominant.getAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE ) ); + } + + /** + * @param str The string to be checked. + * @return true in case string is empty false otherwise. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + + + +} diff --git a/Java/maven-shared-utils-Xpp3DomUtils_141/metadata.json b/Java/maven-shared-utils-Xpp3DomUtils_141/metadata.json new file mode 100644 index 000000000..6b65b7524 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomUtils_141/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Xpp3DomUtils_141", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomUtils.java", + "line": 141, + "npe_method": "isMergeChildren", + "deref_field": "override", + "npe_class": "Xpp3DomUtils", + "repo": "maven-shared-utils", + "bug_id": "Xpp3DomUtils_141" + } +} diff --git a/Java/maven-shared-utils-Xpp3DomUtils_141/npe.json b/Java/maven-shared-utils-Xpp3DomUtils_141/npe.json new file mode 100644 index 000000000..2fbc56667 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomUtils_141/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomUtils.java", + "line": 141, + "npe_method": "isMergeChildren", + "deref_field": "override", + "npe_class": "Xpp3DomUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Xpp3DomUtils_89/Dockerfile b/Java/maven-shared-utils-Xpp3DomUtils_89/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomUtils_89/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Xpp3DomUtils_89/buggy.java b/Java/maven-shared-utils-Xpp3DomUtils_89/buggy.java new file mode 100644 index 000000000..503082566 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomUtils_89/buggy.java @@ -0,0 +1,154 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * + */ +public class Xpp3DomUtils +{ + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @param childMergeOverride true/false. + * @return Merged dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return dominant != null ? merge( dominant, recessive, childMergeOverride ) : recessive; + } + + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @return Merged dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return dominant != null ? merge( dominant, recessive, null ) : recessive; + } + + /** + * @param dominant {@link Xpp3Dom} + * @param recessive {@link Xpp3Dom} + * @param childMergeOverride true/false. + * @return Merged dom. + */ +/** + * + * + * @param dominant + * {@link Xpp3Dom} + * @param recessive + * {@link Xpp3Dom} + * @param childMergeOverride + * true/false. + * @return Merged dom. + */ +public static org.apache.maven.shared.utils.xml.Xpp3Dom merge(org.apache.maven.shared.utils.xml.Xpp3Dom dominant, org.apache.maven.shared.utils.xml.Xpp3Dom recessive, java.lang.Boolean childMergeOverride) { + if ((recessive == null) || org.apache.maven.shared.utils.xml.Xpp3DomUtils.isCombineSelfOverride(dominant)) { + return dominant; + } + if (org.apache.maven.shared.utils.xml.Xpp3DomUtils.isEmpty(dominant.getValue())) { + dominant.setValue(recessive.getValue()); + } + for (java.lang.String attr : recessive.getAttributeNames()) { + if (org.apache.maven.shared.utils.xml.Xpp3DomUtils.isEmpty(dominant.getAttribute(attr))) { + dominant.setAttribute(attr, recessive.getAttribute(attr)); + } + } + if (recessive.getChildCount() > 0) { + boolean mergeChildren = org.apache.maven.shared.utils.xml.Xpp3DomUtils.isMergeChildren(dominant, childMergeOverride); + if (mergeChildren) { + java.util.Map> commonChildren = org.apache.maven.shared.utils.xml.Xpp3DomUtils.getCommonChildren(dominant, recessive); + for (org.apache.maven.shared.utils.xml.Xpp3Dom recessiveChild : recessive) { + java.util.Iterator it = commonChildren.get(recessiveChild.getName()); + { + if (/* NPEX_NULL_EXP */ + it.hasNext()) { + org.apache.maven.shared.utils.xml.Xpp3Dom dominantChild = it.next(); + org.apache.maven.shared.utils.xml.Xpp3DomUtils.merge(dominantChild, recessiveChild, childMergeOverride); + } + } + } + } else { + org.apache.maven.shared.utils.xml.Xpp3Dom[] dominantChildren = dominant.getChildren(); + dominant.childList.clear(); + for (org.apache.maven.shared.utils.xml.Xpp3Dom child : recessive) { + dominant.addChild(new org.apache.maven.shared.utils.xml.Xpp3Dom(child)); + } + for (org.apache.maven.shared.utils.xml.Xpp3Dom aDominantChildren : dominantChildren) { + dominant.addChild(aDominantChildren); + } + } + } + return dominant; +} + + private static Map> getCommonChildren( Xpp3Dom dominant, Xpp3Dom recessive ) + { + Map> commonChildren = new HashMap>(); + + for ( String childName : recessive.childMap.keySet() ) + { + List dominantChildren = dominant.getChildrenList( childName ); + if ( dominantChildren.size() > 0 ) + { + commonChildren.put( childName, dominantChildren.iterator() ); + } + } + return commonChildren; + } + + private static boolean isCombineSelfOverride( Xpp3Dom xpp3Dom ) + { + String selfMergeMode = xpp3Dom.getAttribute( Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE ); + return Xpp3Dom.SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ); + } + + private static boolean isMergeChildren( Xpp3Dom dominant, Boolean override ) + { + return override != null ? override : !isMergeChildren( dominant ); + } + + private static boolean isMergeChildren( Xpp3Dom dominant ) + { + return Xpp3Dom.CHILDREN_COMBINATION_APPEND.equals( + dominant.getAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE ) ); + } + + /** + * @param str The string to be checked. + * @return true in case string is empty false otherwise. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + + + +} diff --git a/Java/maven-shared-utils-Xpp3DomUtils_89/metadata.json b/Java/maven-shared-utils-Xpp3DomUtils_89/metadata.json new file mode 100644 index 000000000..093164d23 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomUtils_89/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Xpp3DomUtils_89", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomUtils.java", + "line": 90, + "npe_method": "merge", + "deref_field": "it", + "npe_class": "Xpp3DomUtils", + "repo": "maven-shared-utils", + "bug_id": "Xpp3DomUtils_89" + } +} diff --git a/Java/maven-shared-utils-Xpp3DomUtils_89/npe.json b/Java/maven-shared-utils-Xpp3DomUtils_89/npe.json new file mode 100644 index 000000000..e30a1e0e4 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomUtils_89/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomUtils.java", + "line": 90, + "npe_method": "merge", + "deref_field": "it", + "npe_class": "Xpp3DomUtils" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Xpp3DomWriter_82/Dockerfile b/Java/maven-shared-utils-Xpp3DomWriter_82/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomWriter_82/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Xpp3DomWriter_82/buggy.java b/Java/maven-shared-utils-Xpp3DomWriter_82/buggy.java new file mode 100644 index 000000000..1a8be1b49 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomWriter_82/buggy.java @@ -0,0 +1,101 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +/** + * @author Brett Porter + */ +public class Xpp3DomWriter +{ + /** + * @param writer {@link Writer} + * @param dom {@link Xpp3Dom} + * @throws IOException if writing fails. + */ + public static void write( Writer writer, Xpp3Dom dom ) throws IOException + { + write( new PrettyPrintXMLWriter( writer ), dom ); + } + + /** + * @param writer {@link PrintWriter} + * @param dom {@link Xpp3Dom} + * @throws IOException if writing fails. + */ + public static void write( PrintWriter writer, Xpp3Dom dom ) throws IOException + { + write( new PrettyPrintXMLWriter( writer ), dom ); + } + + /** + * @param xmlWriter {@link XMLWriter} + * @param dom {@link Xpp3Dom} + * @throws IOException if writing fails. + */ + public static void write( XMLWriter xmlWriter, Xpp3Dom dom ) throws IOException + { + write( xmlWriter, dom, true ); + } + + /** + * @param xmlWriter {@link XMLWriter} + * @param dom {@link Xpp3Dom} + * @param escape true/false. + * @throws IOException if writing fails. + */ +/** + * + * + * @param xmlWriter + * {@link XMLWriter} + * @param dom + * {@link Xpp3Dom} + * @param escape + * true/false. + * @throws IOException + * if writing fails. + */ +public static void write(org.apache.maven.shared.utils.xml.XMLWriter xmlWriter, org.apache.maven.shared.utils.xml.Xpp3Dom dom, boolean escape) throws java.io.IOException { + xmlWriter.startElement(dom.getName()); + java.lang.String[] attributeNames = dom.getAttributeNames(); + for (java.lang.String attributeName : attributeNames) { + xmlWriter.addAttribute(attributeName, dom.getAttribute(attributeName)); + } + org.apache.maven.shared.utils.xml.Xpp3Dom[] children = dom.getChildren(); + for (org.apache.maven.shared.utils.xml.Xpp3Dom aChildren : children) { + org.apache.maven.shared.utils.xml.Xpp3DomWriter.write(xmlWriter, aChildren, escape); + } + java.lang.String value = dom.getValue(); + { + if (escape) { + xmlWriter.writeText(/* NPEX_NULL_EXP */ + value); + } else { + xmlWriter.writeMarkup(value); + } + } + xmlWriter.endElement(); +} + +} diff --git a/Java/maven-shared-utils-Xpp3DomWriter_82/metadata.json b/Java/maven-shared-utils-Xpp3DomWriter_82/metadata.json new file mode 100644 index 000000000..e54c277cf --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomWriter_82/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Xpp3DomWriter_82", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomWriter.java", + "line": 93, + "npe_method": "write", + "deref_field": "value", + "npe_class": "Xpp3DomWriter", + "repo": "maven-shared-utils", + "bug_id": "Xpp3DomWriter_82" + } +} diff --git a/Java/maven-shared-utils-Xpp3DomWriter_82/npe.json b/Java/maven-shared-utils-Xpp3DomWriter_82/npe.json new file mode 100644 index 000000000..96526c600 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3DomWriter_82/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3DomWriter.java", + "line": 93, + "npe_method": "write", + "deref_field": "value", + "npe_class": "Xpp3DomWriter" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Xpp3Dom_177/Dockerfile b/Java/maven-shared-utils-Xpp3Dom_177/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_177/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Xpp3Dom_177/buggy.java b/Java/maven-shared-utils-Xpp3Dom_177/buggy.java new file mode 100644 index 000000000..424690cff --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_177/buggy.java @@ -0,0 +1,436 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +/** + * A reimplementation of Plexus Xpp3Dom based on the public interface of Plexus Xpp3Dom. + * + * @author Kristian Rosenvold + */ +public class Xpp3Dom + implements Iterable +{ + private static final long serialVersionUID = 2567894443061173996L; + + private String name; // plexus: protected + + private String value; // plexus: protected + + private Map attributes; // plexus: protected + + final List childList; // plexus: protected + + final Map childMap; // plexus: protected + + private Xpp3Dom parent; // plexus: protected + + /** + * The attribute which identifies merge/append. + */ + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; + + private static final String CHILDREN_COMBINATION_MERGE = "merge"; + + /** + * The attribute append. + */ + public static final String CHILDREN_COMBINATION_APPEND = "append"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; // plexus: public + + /** + * The name of the attribute. + */ + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + + /** + * The attributes which identifies override. + */ + public static final String SELF_COMBINATION_OVERRIDE = "override"; // plexus: public + + /** + * The attribute which identifies merge + */ + public static final String SELF_COMBINATION_MERGE = "merge"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; // plexus: public + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0]; + + /** + * @param name The name of the instance. + */ + public Xpp3Dom( String name ) + { + this.name = name; + childList = new ArrayList(); + childMap = new HashMap(); + } + + /** + * Create instance. + * @param source The source. + */ + public Xpp3Dom( Xpp3Dom source ) + { + this( source, source.getName() ); + } + + /** + * Create instance. + * @param src The source Dom. + * @param name The name of the Dom. + */ + public Xpp3Dom( @Nonnull Xpp3Dom src, String name ) + { + this.name = name; + + int size = src.getChildCount(); + childList = new ArrayList( size ); + childMap = new HashMap(); + + setValue( src.getValue() ); + + for ( String attributeName : src.getAttributeNames() ) + { + setAttribute( attributeName, src.getAttribute( attributeName ) ); + } + + for ( Xpp3Dom xpp3Dom : src.getChildren() ) + { + addChild( new Xpp3Dom( xpp3Dom ) ); + } + } + + /** + * @return The current name. + */ + public String getName() + { + return name; + } + + /** + * @return The current value. + */ + @Nonnull public String getValue() + { + return value; + } + + /** + * @param value The value to be set. + */ + public void setValue( @Nonnull String value ) + { + this.value = value; + } + + + /** + * @return The array of attribute names. + */ + public String[] getAttributeNames() + { + boolean isNothing = attributes == null || attributes.isEmpty(); + return isNothing ? EMPTY_STRING_ARRAY : attributes.keySet().toArray( new String[attributes.size()] ); + } + + + /** + * @param nameParameter The name of the attribute. + * @return The attribute value. + */ +/** + * + * + * @param nameParameter + * The name of the attribute. + * @return The attribute value. + */ +public java.lang.String getAttribute(java.lang.String nameParameter) { + return /* NPEX_NULL_EXP */ + this.attributes.get(nameParameter); +} + + /** + * @param nameParameter The name of the attribute. + * @param valueParameter The value of the attribute. + */ + public void setAttribute( @Nonnull String nameParameter, @Nonnull String valueParameter ) + { + if ( valueParameter == null ) + { + throw new NullPointerException( "value can not be null" ); + } + if ( nameParameter == null ) + { + throw new NullPointerException( "name can not be null" ); + } + if ( attributes == null ) + { + attributes = new HashMap(); + } + + attributes.put( nameParameter, valueParameter ); + } + + /** + * @param i The index to be selected. + * @return The child selected by index. + */ + public Xpp3Dom getChild( int i ) + { + return childList.get( i ); + } + + /** + * @param nameParameter The name of the child. + * @return The child selected by name. + */ + public Xpp3Dom getChild( String nameParameter ) + { + return childMap.get( nameParameter ); + } + + /** + * @param child The child to be added. + */ + public void addChild( Xpp3Dom child ) + { + child.setParent( this ); + childList.add( child ); + childMap.put( child.getName(), child ); + } + + /** + * @return The array of childs. + */ + public Xpp3Dom[] getChildren() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? EMPTY_DOM_ARRAY : childList.toArray( new Xpp3Dom[childList.size()] ); + } + + private List getChildrenList() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? Collections.emptyList() : childList; + } + + /** + * @param nameParameter The name of the child. + * @return The array of the Dom. + */ + public Xpp3Dom[] getChildren( String nameParameter ) + { + List children = getChildrenList( nameParameter ); + return children.toArray( new Xpp3Dom[children.size()] ); + } + + List getChildrenList( String nameParameter ) + { + if ( childList == null ) + { + return Collections.emptyList(); + } + else + { + ArrayList children = new ArrayList(); + for ( Xpp3Dom aChildList : childList ) + { + if ( nameParameter.equals( aChildList.getName() ) ) + { + children.add( aChildList ); + } + } + return children; + } + } + + /** + * @return The number of childs. + */ + public int getChildCount() + { + if ( childList == null ) + { + return 0; + } + + return childList.size(); + } + + /** + * @param i The child to be removed. + */ + public void removeChild( int i ) + { + Xpp3Dom child = childList.remove( i ); + childMap.values().remove( child ); + child.setParent( null ); + } + + /** + * @return The current parent. + */ + public Xpp3Dom getParent() + { + return parent; + } + + /** + * @param parent Set the parent. + */ + public void setParent( Xpp3Dom parent ) + { + this.parent = parent; + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @param childMergeOverride true if child merge will take precedence false otherwise. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive, childMergeOverride ); + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive ); + } + + /** {@inheritDoc} */ + public boolean equals( Object obj ) + { + if ( obj == this ) + { + return true; + } + + if ( !( obj instanceof Xpp3Dom ) ) + { + return false; + } + + Xpp3Dom dom = (Xpp3Dom) obj; + + return !( name == null ? dom.name != null : !name.equals( dom.name ) ) + && !( value == null ? dom.value != null : !value.equals( dom.value ) ) + && !( attributes == null ? dom.attributes != null : !attributes.equals( dom.attributes ) ) + && !( childList == null ? dom.childList != null : !childList.equals( dom.childList ) ); + } + + /** {@inheritDoc} */ + public int hashCode() + { + int result = 17; + result = 37 * result + ( name != null ? name.hashCode() : 0 ); + result = 37 * result + ( value != null ? value.hashCode() : 0 ); + result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 ); + result = 37 * result + ( childList != null ? childList.hashCode() : 0 ); + return result; + } + + /** {@inheritDoc} */ + public String toString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + /** + * @return Unescaped string. + */ + public String toUnescapedString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this, false ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + private PrettyPrintXMLWriter getPrettyPrintXMLWriter( StringWriter writer ) + { + return new PrettyPrintXMLWriter( writer, "UTF-8", null ); + } + + /** + * @param str The string to be checked. + * @return true if the string is not empty (length > 0) and not null. + */ + public static boolean isNotEmpty( String str ) + { + return str != null && str.length() > 0; + } + + /** + * @param str The string to be checked. + * @return true if the string is empty or null. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + /** {@inheritDoc} */ + public Iterator iterator() + { + return getChildrenList().iterator(); + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_177/metadata.json b/Java/maven-shared-utils-Xpp3Dom_177/metadata.json new file mode 100644 index 000000000..e9941073a --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_177/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Xpp3Dom_177", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 184, + "npe_method": "getAttribute", + "deref_field": "this.attributes", + "npe_class": "Xpp3Dom", + "repo": "maven-shared-utils", + "bug_id": "Xpp3Dom_177" + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_177/npe.json b/Java/maven-shared-utils-Xpp3Dom_177/npe.json new file mode 100644 index 000000000..fac09647c --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_177/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 184, + "npe_method": "getAttribute", + "deref_field": "this.attributes", + "npe_class": "Xpp3Dom" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Xpp3Dom_186/Dockerfile b/Java/maven-shared-utils-Xpp3Dom_186/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_186/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Xpp3Dom_186/buggy.java b/Java/maven-shared-utils-Xpp3Dom_186/buggy.java new file mode 100644 index 000000000..743a5a250 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_186/buggy.java @@ -0,0 +1,434 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +/** + * A reimplementation of Plexus Xpp3Dom based on the public interface of Plexus Xpp3Dom. + * + * @author Kristian Rosenvold + */ +public class Xpp3Dom + implements Iterable +{ + private static final long serialVersionUID = 2567894443061173996L; + + private String name; // plexus: protected + + private String value; // plexus: protected + + private Map attributes; // plexus: protected + + final List childList; // plexus: protected + + final Map childMap; // plexus: protected + + private Xpp3Dom parent; // plexus: protected + + /** + * The attribute which identifies merge/append. + */ + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; + + private static final String CHILDREN_COMBINATION_MERGE = "merge"; + + /** + * The attribute append. + */ + public static final String CHILDREN_COMBINATION_APPEND = "append"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; // plexus: public + + /** + * The name of the attribute. + */ + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + + /** + * The attributes which identifies override. + */ + public static final String SELF_COMBINATION_OVERRIDE = "override"; // plexus: public + + /** + * The attribute which identifies merge + */ + public static final String SELF_COMBINATION_MERGE = "merge"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; // plexus: public + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0]; + + /** + * @param name The name of the instance. + */ + public Xpp3Dom( String name ) + { + this.name = name; + childList = new ArrayList(); + childMap = new HashMap(); + } + + /** + * Create instance. + * @param source The source. + */ + public Xpp3Dom( Xpp3Dom source ) + { + this( source, source.getName() ); + } + + /** + * Create instance. + * @param src The source Dom. + * @param name The name of the Dom. + */ + public Xpp3Dom( @Nonnull Xpp3Dom src, String name ) + { + this.name = name; + + int size = src.getChildCount(); + childList = new ArrayList( size ); + childMap = new HashMap(); + + setValue( src.getValue() ); + + for ( String attributeName : src.getAttributeNames() ) + { + setAttribute( attributeName, src.getAttribute( attributeName ) ); + } + + for ( Xpp3Dom xpp3Dom : src.getChildren() ) + { + addChild( new Xpp3Dom( xpp3Dom ) ); + } + } + + /** + * @return The current name. + */ + public String getName() + { + return name; + } + + /** + * @return The current value. + */ + @Nonnull public String getValue() + { + return value; + } + + /** + * @param value The value to be set. + */ + public void setValue( @Nonnull String value ) + { + this.value = value; + } + + + /** + * @return The array of attribute names. + */ + public String[] getAttributeNames() + { + boolean isNothing = attributes == null || attributes.isEmpty(); + return isNothing ? EMPTY_STRING_ARRAY : attributes.keySet().toArray( new String[attributes.size()] ); + } + + + /** + * @param nameParameter The name of the attribute. + * @return The attribute value. + */ + public String getAttribute( String nameParameter ) + { + return this.attributes != null ? this.attributes.get( nameParameter ) : null; + } + + /** + * @param nameParameter The name of the attribute. + * @param valueParameter The value of the attribute. + */ +/** + * + * + * @param nameParameter + * The name of the attribute. + * @param valueParameter + * The value of the attribute. + */ +public void setAttribute(@javax.annotation.Nonnull +java.lang.String nameParameter, @javax.annotation.Nonnull +java.lang.String valueParameter) { + { + if (nameParameter == null) { + throw new java.lang.NullPointerException("name can not be null"); + } + if (attributes == null) { + attributes = new java.util.HashMap(); + } + attributes.put(nameParameter, /* NPEX_NULL_EXP */ + valueParameter); + } +} + + /** + * @param i The index to be selected. + * @return The child selected by index. + */ + public Xpp3Dom getChild( int i ) + { + return childList.get( i ); + } + + /** + * @param nameParameter The name of the child. + * @return The child selected by name. + */ + public Xpp3Dom getChild( String nameParameter ) + { + return childMap.get( nameParameter ); + } + + /** + * @param child The child to be added. + */ + public void addChild( Xpp3Dom child ) + { + child.setParent( this ); + childList.add( child ); + childMap.put( child.getName(), child ); + } + + /** + * @return The array of childs. + */ + public Xpp3Dom[] getChildren() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? EMPTY_DOM_ARRAY : childList.toArray( new Xpp3Dom[childList.size()] ); + } + + private List getChildrenList() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? Collections.emptyList() : childList; + } + + /** + * @param nameParameter The name of the child. + * @return The array of the Dom. + */ + public Xpp3Dom[] getChildren( String nameParameter ) + { + List children = getChildrenList( nameParameter ); + return children.toArray( new Xpp3Dom[children.size()] ); + } + + List getChildrenList( String nameParameter ) + { + if ( childList == null ) + { + return Collections.emptyList(); + } + else + { + ArrayList children = new ArrayList(); + for ( Xpp3Dom aChildList : childList ) + { + if ( nameParameter.equals( aChildList.getName() ) ) + { + children.add( aChildList ); + } + } + return children; + } + } + + /** + * @return The number of childs. + */ + public int getChildCount() + { + if ( childList == null ) + { + return 0; + } + + return childList.size(); + } + + /** + * @param i The child to be removed. + */ + public void removeChild( int i ) + { + Xpp3Dom child = childList.remove( i ); + childMap.values().remove( child ); + child.setParent( null ); + } + + /** + * @return The current parent. + */ + public Xpp3Dom getParent() + { + return parent; + } + + /** + * @param parent Set the parent. + */ + public void setParent( Xpp3Dom parent ) + { + this.parent = parent; + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @param childMergeOverride true if child merge will take precedence false otherwise. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive, childMergeOverride ); + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive ); + } + + /** {@inheritDoc} */ + public boolean equals( Object obj ) + { + if ( obj == this ) + { + return true; + } + + if ( !( obj instanceof Xpp3Dom ) ) + { + return false; + } + + Xpp3Dom dom = (Xpp3Dom) obj; + + return !( name == null ? dom.name != null : !name.equals( dom.name ) ) + && !( value == null ? dom.value != null : !value.equals( dom.value ) ) + && !( attributes == null ? dom.attributes != null : !attributes.equals( dom.attributes ) ) + && !( childList == null ? dom.childList != null : !childList.equals( dom.childList ) ); + } + + /** {@inheritDoc} */ + public int hashCode() + { + int result = 17; + result = 37 * result + ( name != null ? name.hashCode() : 0 ); + result = 37 * result + ( value != null ? value.hashCode() : 0 ); + result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 ); + result = 37 * result + ( childList != null ? childList.hashCode() : 0 ); + return result; + } + + /** {@inheritDoc} */ + public String toString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + /** + * @return Unescaped string. + */ + public String toUnescapedString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this, false ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + private PrettyPrintXMLWriter getPrettyPrintXMLWriter( StringWriter writer ) + { + return new PrettyPrintXMLWriter( writer, "UTF-8", null ); + } + + /** + * @param str The string to be checked. + * @return true if the string is not empty (length > 0) and not null. + */ + public static boolean isNotEmpty( String str ) + { + return str != null && str.length() > 0; + } + + /** + * @param str The string to be checked. + * @return true if the string is empty or null. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + /** {@inheritDoc} */ + public Iterator iterator() + { + return getChildrenList().iterator(); + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_186/metadata.json b/Java/maven-shared-utils-Xpp3Dom_186/metadata.json new file mode 100644 index 000000000..7cc17a36e --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_186/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Xpp3Dom_186", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 203, + "npe_method": "setAttribute", + "deref_field": "valueParameter", + "npe_class": "Xpp3Dom", + "repo": "maven-shared-utils", + "bug_id": "Xpp3Dom_186" + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_186/npe.json b/Java/maven-shared-utils-Xpp3Dom_186/npe.json new file mode 100644 index 000000000..62f706b65 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_186/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 203, + "npe_method": "setAttribute", + "deref_field": "valueParameter", + "npe_class": "Xpp3Dom" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Xpp3Dom_190/Dockerfile b/Java/maven-shared-utils-Xpp3Dom_190/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_190/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Xpp3Dom_190/buggy.java b/Java/maven-shared-utils-Xpp3Dom_190/buggy.java new file mode 100644 index 000000000..bf7815323 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_190/buggy.java @@ -0,0 +1,434 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +/** + * A reimplementation of Plexus Xpp3Dom based on the public interface of Plexus Xpp3Dom. + * + * @author Kristian Rosenvold + */ +public class Xpp3Dom + implements Iterable +{ + private static final long serialVersionUID = 2567894443061173996L; + + private String name; // plexus: protected + + private String value; // plexus: protected + + private Map attributes; // plexus: protected + + final List childList; // plexus: protected + + final Map childMap; // plexus: protected + + private Xpp3Dom parent; // plexus: protected + + /** + * The attribute which identifies merge/append. + */ + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; + + private static final String CHILDREN_COMBINATION_MERGE = "merge"; + + /** + * The attribute append. + */ + public static final String CHILDREN_COMBINATION_APPEND = "append"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; // plexus: public + + /** + * The name of the attribute. + */ + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + + /** + * The attributes which identifies override. + */ + public static final String SELF_COMBINATION_OVERRIDE = "override"; // plexus: public + + /** + * The attribute which identifies merge + */ + public static final String SELF_COMBINATION_MERGE = "merge"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; // plexus: public + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0]; + + /** + * @param name The name of the instance. + */ + public Xpp3Dom( String name ) + { + this.name = name; + childList = new ArrayList(); + childMap = new HashMap(); + } + + /** + * Create instance. + * @param source The source. + */ + public Xpp3Dom( Xpp3Dom source ) + { + this( source, source.getName() ); + } + + /** + * Create instance. + * @param src The source Dom. + * @param name The name of the Dom. + */ + public Xpp3Dom( @Nonnull Xpp3Dom src, String name ) + { + this.name = name; + + int size = src.getChildCount(); + childList = new ArrayList( size ); + childMap = new HashMap(); + + setValue( src.getValue() ); + + for ( String attributeName : src.getAttributeNames() ) + { + setAttribute( attributeName, src.getAttribute( attributeName ) ); + } + + for ( Xpp3Dom xpp3Dom : src.getChildren() ) + { + addChild( new Xpp3Dom( xpp3Dom ) ); + } + } + + /** + * @return The current name. + */ + public String getName() + { + return name; + } + + /** + * @return The current value. + */ + @Nonnull public String getValue() + { + return value; + } + + /** + * @param value The value to be set. + */ + public void setValue( @Nonnull String value ) + { + this.value = value; + } + + + /** + * @return The array of attribute names. + */ + public String[] getAttributeNames() + { + boolean isNothing = attributes == null || attributes.isEmpty(); + return isNothing ? EMPTY_STRING_ARRAY : attributes.keySet().toArray( new String[attributes.size()] ); + } + + + /** + * @param nameParameter The name of the attribute. + * @return The attribute value. + */ + public String getAttribute( String nameParameter ) + { + return this.attributes != null ? this.attributes.get( nameParameter ) : null; + } + + /** + * @param nameParameter The name of the attribute. + * @param valueParameter The value of the attribute. + */ +/** + * + * + * @param nameParameter + * The name of the attribute. + * @param valueParameter + * The value of the attribute. + */ +public void setAttribute(@javax.annotation.Nonnull +java.lang.String nameParameter, @javax.annotation.Nonnull +java.lang.String valueParameter) { + if (valueParameter == null) { + throw new java.lang.NullPointerException("value can not be null"); + } + { + if (attributes == null) { + attributes = new java.util.HashMap(); + } + attributes.put(/* NPEX_NULL_EXP */ + nameParameter, valueParameter); + } +} + + /** + * @param i The index to be selected. + * @return The child selected by index. + */ + public Xpp3Dom getChild( int i ) + { + return childList.get( i ); + } + + /** + * @param nameParameter The name of the child. + * @return The child selected by name. + */ + public Xpp3Dom getChild( String nameParameter ) + { + return childMap.get( nameParameter ); + } + + /** + * @param child The child to be added. + */ + public void addChild( Xpp3Dom child ) + { + child.setParent( this ); + childList.add( child ); + childMap.put( child.getName(), child ); + } + + /** + * @return The array of childs. + */ + public Xpp3Dom[] getChildren() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? EMPTY_DOM_ARRAY : childList.toArray( new Xpp3Dom[childList.size()] ); + } + + private List getChildrenList() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? Collections.emptyList() : childList; + } + + /** + * @param nameParameter The name of the child. + * @return The array of the Dom. + */ + public Xpp3Dom[] getChildren( String nameParameter ) + { + List children = getChildrenList( nameParameter ); + return children.toArray( new Xpp3Dom[children.size()] ); + } + + List getChildrenList( String nameParameter ) + { + if ( childList == null ) + { + return Collections.emptyList(); + } + else + { + ArrayList children = new ArrayList(); + for ( Xpp3Dom aChildList : childList ) + { + if ( nameParameter.equals( aChildList.getName() ) ) + { + children.add( aChildList ); + } + } + return children; + } + } + + /** + * @return The number of childs. + */ + public int getChildCount() + { + if ( childList == null ) + { + return 0; + } + + return childList.size(); + } + + /** + * @param i The child to be removed. + */ + public void removeChild( int i ) + { + Xpp3Dom child = childList.remove( i ); + childMap.values().remove( child ); + child.setParent( null ); + } + + /** + * @return The current parent. + */ + public Xpp3Dom getParent() + { + return parent; + } + + /** + * @param parent Set the parent. + */ + public void setParent( Xpp3Dom parent ) + { + this.parent = parent; + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @param childMergeOverride true if child merge will take precedence false otherwise. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive, childMergeOverride ); + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive ); + } + + /** {@inheritDoc} */ + public boolean equals( Object obj ) + { + if ( obj == this ) + { + return true; + } + + if ( !( obj instanceof Xpp3Dom ) ) + { + return false; + } + + Xpp3Dom dom = (Xpp3Dom) obj; + + return !( name == null ? dom.name != null : !name.equals( dom.name ) ) + && !( value == null ? dom.value != null : !value.equals( dom.value ) ) + && !( attributes == null ? dom.attributes != null : !attributes.equals( dom.attributes ) ) + && !( childList == null ? dom.childList != null : !childList.equals( dom.childList ) ); + } + + /** {@inheritDoc} */ + public int hashCode() + { + int result = 17; + result = 37 * result + ( name != null ? name.hashCode() : 0 ); + result = 37 * result + ( value != null ? value.hashCode() : 0 ); + result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 ); + result = 37 * result + ( childList != null ? childList.hashCode() : 0 ); + return result; + } + + /** {@inheritDoc} */ + public String toString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + /** + * @return Unescaped string. + */ + public String toUnescapedString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this, false ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + private PrettyPrintXMLWriter getPrettyPrintXMLWriter( StringWriter writer ) + { + return new PrettyPrintXMLWriter( writer, "UTF-8", null ); + } + + /** + * @param str The string to be checked. + * @return true if the string is not empty (length > 0) and not null. + */ + public static boolean isNotEmpty( String str ) + { + return str != null && str.length() > 0; + } + + /** + * @param str The string to be checked. + * @return true if the string is empty or null. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + /** {@inheritDoc} */ + public Iterator iterator() + { + return getChildrenList().iterator(); + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_190/metadata.json b/Java/maven-shared-utils-Xpp3Dom_190/metadata.json new file mode 100644 index 000000000..503fb05c4 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_190/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Xpp3Dom_190", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 203, + "npe_method": "setAttribute", + "deref_field": "nameParameter", + "npe_class": "Xpp3Dom", + "repo": "maven-shared-utils", + "bug_id": "Xpp3Dom_190" + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_190/npe.json b/Java/maven-shared-utils-Xpp3Dom_190/npe.json new file mode 100644 index 000000000..c140f9f81 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_190/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 203, + "npe_method": "setAttribute", + "deref_field": "nameParameter", + "npe_class": "Xpp3Dom" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Xpp3Dom_351/Dockerfile b/Java/maven-shared-utils-Xpp3Dom_351/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_351/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Xpp3Dom_351/buggy.java b/Java/maven-shared-utils-Xpp3Dom_351/buggy.java new file mode 100644 index 000000000..cf014709f --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_351/buggy.java @@ -0,0 +1,424 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +/** + * A reimplementation of Plexus Xpp3Dom based on the public interface of Plexus Xpp3Dom. + * + * @author Kristian Rosenvold + */ +public class Xpp3Dom + implements Iterable +{ + private static final long serialVersionUID = 2567894443061173996L; + + private String name; // plexus: protected + + private String value; // plexus: protected + + private Map attributes; // plexus: protected + + final List childList; // plexus: protected + + final Map childMap; // plexus: protected + + private Xpp3Dom parent; // plexus: protected + + /** + * The attribute which identifies merge/append. + */ + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; + + private static final String CHILDREN_COMBINATION_MERGE = "merge"; + + /** + * The attribute append. + */ + public static final String CHILDREN_COMBINATION_APPEND = "append"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; // plexus: public + + /** + * The name of the attribute. + */ + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + + /** + * The attributes which identifies override. + */ + public static final String SELF_COMBINATION_OVERRIDE = "override"; // plexus: public + + /** + * The attribute which identifies merge + */ + public static final String SELF_COMBINATION_MERGE = "merge"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; // plexus: public + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0]; + + /** + * @param name The name of the instance. + */ + public Xpp3Dom( String name ) + { + this.name = name; + childList = new ArrayList(); + childMap = new HashMap(); + } + + /** + * Create instance. + * @param source The source. + */ + public Xpp3Dom( Xpp3Dom source ) + { + this( source, source.getName() ); + } + + /** + * Create instance. + * @param src The source Dom. + * @param name The name of the Dom. + */ + public Xpp3Dom( @Nonnull Xpp3Dom src, String name ) + { + this.name = name; + + int size = src.getChildCount(); + childList = new ArrayList( size ); + childMap = new HashMap(); + + setValue( src.getValue() ); + + for ( String attributeName : src.getAttributeNames() ) + { + setAttribute( attributeName, src.getAttribute( attributeName ) ); + } + + for ( Xpp3Dom xpp3Dom : src.getChildren() ) + { + addChild( new Xpp3Dom( xpp3Dom ) ); + } + } + + /** + * @return The current name. + */ + public String getName() + { + return name; + } + + /** + * @return The current value. + */ + @Nonnull public String getValue() + { + return value; + } + + /** + * @param value The value to be set. + */ + public void setValue( @Nonnull String value ) + { + this.value = value; + } + + + /** + * @return The array of attribute names. + */ + public String[] getAttributeNames() + { + boolean isNothing = attributes == null || attributes.isEmpty(); + return isNothing ? EMPTY_STRING_ARRAY : attributes.keySet().toArray( new String[attributes.size()] ); + } + + + /** + * @param nameParameter The name of the attribute. + * @return The attribute value. + */ + public String getAttribute( String nameParameter ) + { + return this.attributes != null ? this.attributes.get( nameParameter ) : null; + } + + /** + * @param nameParameter The name of the attribute. + * @param valueParameter The value of the attribute. + */ + public void setAttribute( @Nonnull String nameParameter, @Nonnull String valueParameter ) + { + if ( valueParameter == null ) + { + throw new NullPointerException( "value can not be null" ); + } + if ( nameParameter == null ) + { + throw new NullPointerException( "name can not be null" ); + } + if ( attributes == null ) + { + attributes = new HashMap(); + } + + attributes.put( nameParameter, valueParameter ); + } + + /** + * @param i The index to be selected. + * @return The child selected by index. + */ + public Xpp3Dom getChild( int i ) + { + return childList.get( i ); + } + + /** + * @param nameParameter The name of the child. + * @return The child selected by name. + */ + public Xpp3Dom getChild( String nameParameter ) + { + return childMap.get( nameParameter ); + } + + /** + * @param child The child to be added. + */ + public void addChild( Xpp3Dom child ) + { + child.setParent( this ); + childList.add( child ); + childMap.put( child.getName(), child ); + } + + /** + * @return The array of childs. + */ + public Xpp3Dom[] getChildren() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? EMPTY_DOM_ARRAY : childList.toArray( new Xpp3Dom[childList.size()] ); + } + + private List getChildrenList() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? Collections.emptyList() : childList; + } + + /** + * @param nameParameter The name of the child. + * @return The array of the Dom. + */ + public Xpp3Dom[] getChildren( String nameParameter ) + { + List children = getChildrenList( nameParameter ); + return children.toArray( new Xpp3Dom[children.size()] ); + } + + List getChildrenList( String nameParameter ) + { + if ( childList == null ) + { + return Collections.emptyList(); + } + else + { + ArrayList children = new ArrayList(); + for ( Xpp3Dom aChildList : childList ) + { + if ( nameParameter.equals( aChildList.getName() ) ) + { + children.add( aChildList ); + } + } + return children; + } + } + + /** + * @return The number of childs. + */ + public int getChildCount() + { + if ( childList == null ) + { + return 0; + } + + return childList.size(); + } + + /** + * @param i The child to be removed. + */ + public void removeChild( int i ) + { + Xpp3Dom child = childList.remove( i ); + childMap.values().remove( child ); + child.setParent( null ); + } + + /** + * @return The current parent. + */ + public Xpp3Dom getParent() + { + return parent; + } + + /** + * @param parent Set the parent. + */ + public void setParent( Xpp3Dom parent ) + { + this.parent = parent; + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @param childMergeOverride true if child merge will take precedence false otherwise. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive, childMergeOverride ); + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public boolean equals(java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.apache.maven.shared.utils.xml.Xpp3Dom)) { + return false; + } + org.apache.maven.shared.utils.xml.Xpp3Dom dom = ((org.apache.maven.shared.utils.xml.Xpp3Dom) (obj)); + return (((!(name == null ? dom.name != null : !name.equals(dom.name))) && (!(!/* NPEX_NULL_EXP */ + value.equals(dom.value)))) && (!(attributes == null ? dom.attributes != null : !attributes.equals(dom.attributes)))) && (!(childList == null ? dom.childList != null : !childList.equals(dom.childList))); +} + + /** {@inheritDoc} */ + public int hashCode() + { + int result = 17; + result = 37 * result + ( name != null ? name.hashCode() : 0 ); + result = 37 * result + ( value != null ? value.hashCode() : 0 ); + result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 ); + result = 37 * result + ( childList != null ? childList.hashCode() : 0 ); + return result; + } + + /** {@inheritDoc} */ + public String toString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + /** + * @return Unescaped string. + */ + public String toUnescapedString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this, false ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + private PrettyPrintXMLWriter getPrettyPrintXMLWriter( StringWriter writer ) + { + return new PrettyPrintXMLWriter( writer, "UTF-8", null ); + } + + /** + * @param str The string to be checked. + * @return true if the string is not empty (length > 0) and not null. + */ + public static boolean isNotEmpty( String str ) + { + return str != null && str.length() > 0; + } + + /** + * @param str The string to be checked. + * @return true if the string is empty or null. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + /** {@inheritDoc} */ + public Iterator iterator() + { + return getChildrenList().iterator(); + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_351/metadata.json b/Java/maven-shared-utils-Xpp3Dom_351/metadata.json new file mode 100644 index 000000000..7e352bbfd --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_351/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Xpp3Dom_351", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 348, + "npe_method": "equals", + "deref_field": "value", + "npe_class": "Xpp3Dom", + "repo": "maven-shared-utils", + "bug_id": "Xpp3Dom_351" + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_351/npe.json b/Java/maven-shared-utils-Xpp3Dom_351/npe.json new file mode 100644 index 000000000..479a9701d --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_351/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 348, + "npe_method": "equals", + "deref_field": "value", + "npe_class": "Xpp3Dom" +} \ No newline at end of file diff --git a/Java/maven-shared-utils-Xpp3Dom_352/Dockerfile b/Java/maven-shared-utils-Xpp3Dom_352/Dockerfile new file mode 100644 index 000000000..4a8f68939 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_352/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-shared-utils + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-shared-utils-Xpp3Dom_352/buggy.java b/Java/maven-shared-utils-Xpp3Dom_352/buggy.java new file mode 100644 index 000000000..614e2a566 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_352/buggy.java @@ -0,0 +1,424 @@ +package org.apache.maven.shared.utils.xml; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +/** + * A reimplementation of Plexus Xpp3Dom based on the public interface of Plexus Xpp3Dom. + * + * @author Kristian Rosenvold + */ +public class Xpp3Dom + implements Iterable +{ + private static final long serialVersionUID = 2567894443061173996L; + + private String name; // plexus: protected + + private String value; // plexus: protected + + private Map attributes; // plexus: protected + + final List childList; // plexus: protected + + final Map childMap; // plexus: protected + + private Xpp3Dom parent; // plexus: protected + + /** + * The attribute which identifies merge/append. + */ + public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children"; + + private static final String CHILDREN_COMBINATION_MERGE = "merge"; + + /** + * The attribute append. + */ + public static final String CHILDREN_COMBINATION_APPEND = "append"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; // plexus: public + + /** + * The name of the attribute. + */ + public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self"; + + /** + * The attributes which identifies override. + */ + public static final String SELF_COMBINATION_OVERRIDE = "override"; // plexus: public + + /** + * The attribute which identifies merge + */ + public static final String SELF_COMBINATION_MERGE = "merge"; + + @SuppressWarnings( "UnusedDeclaration" ) + private static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; // plexus: public + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0]; + + /** + * @param name The name of the instance. + */ + public Xpp3Dom( String name ) + { + this.name = name; + childList = new ArrayList(); + childMap = new HashMap(); + } + + /** + * Create instance. + * @param source The source. + */ + public Xpp3Dom( Xpp3Dom source ) + { + this( source, source.getName() ); + } + + /** + * Create instance. + * @param src The source Dom. + * @param name The name of the Dom. + */ + public Xpp3Dom( @Nonnull Xpp3Dom src, String name ) + { + this.name = name; + + int size = src.getChildCount(); + childList = new ArrayList( size ); + childMap = new HashMap(); + + setValue( src.getValue() ); + + for ( String attributeName : src.getAttributeNames() ) + { + setAttribute( attributeName, src.getAttribute( attributeName ) ); + } + + for ( Xpp3Dom xpp3Dom : src.getChildren() ) + { + addChild( new Xpp3Dom( xpp3Dom ) ); + } + } + + /** + * @return The current name. + */ + public String getName() + { + return name; + } + + /** + * @return The current value. + */ + @Nonnull public String getValue() + { + return value; + } + + /** + * @param value The value to be set. + */ + public void setValue( @Nonnull String value ) + { + this.value = value; + } + + + /** + * @return The array of attribute names. + */ + public String[] getAttributeNames() + { + boolean isNothing = attributes == null || attributes.isEmpty(); + return isNothing ? EMPTY_STRING_ARRAY : attributes.keySet().toArray( new String[attributes.size()] ); + } + + + /** + * @param nameParameter The name of the attribute. + * @return The attribute value. + */ + public String getAttribute( String nameParameter ) + { + return this.attributes != null ? this.attributes.get( nameParameter ) : null; + } + + /** + * @param nameParameter The name of the attribute. + * @param valueParameter The value of the attribute. + */ + public void setAttribute( @Nonnull String nameParameter, @Nonnull String valueParameter ) + { + if ( valueParameter == null ) + { + throw new NullPointerException( "value can not be null" ); + } + if ( nameParameter == null ) + { + throw new NullPointerException( "name can not be null" ); + } + if ( attributes == null ) + { + attributes = new HashMap(); + } + + attributes.put( nameParameter, valueParameter ); + } + + /** + * @param i The index to be selected. + * @return The child selected by index. + */ + public Xpp3Dom getChild( int i ) + { + return childList.get( i ); + } + + /** + * @param nameParameter The name of the child. + * @return The child selected by name. + */ + public Xpp3Dom getChild( String nameParameter ) + { + return childMap.get( nameParameter ); + } + + /** + * @param child The child to be added. + */ + public void addChild( Xpp3Dom child ) + { + child.setParent( this ); + childList.add( child ); + childMap.put( child.getName(), child ); + } + + /** + * @return The array of childs. + */ + public Xpp3Dom[] getChildren() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? EMPTY_DOM_ARRAY : childList.toArray( new Xpp3Dom[childList.size()] ); + } + + private List getChildrenList() + { + boolean isNothing = childList == null || childList.isEmpty(); + return isNothing ? Collections.emptyList() : childList; + } + + /** + * @param nameParameter The name of the child. + * @return The array of the Dom. + */ + public Xpp3Dom[] getChildren( String nameParameter ) + { + List children = getChildrenList( nameParameter ); + return children.toArray( new Xpp3Dom[children.size()] ); + } + + List getChildrenList( String nameParameter ) + { + if ( childList == null ) + { + return Collections.emptyList(); + } + else + { + ArrayList children = new ArrayList(); + for ( Xpp3Dom aChildList : childList ) + { + if ( nameParameter.equals( aChildList.getName() ) ) + { + children.add( aChildList ); + } + } + return children; + } + } + + /** + * @return The number of childs. + */ + public int getChildCount() + { + if ( childList == null ) + { + return 0; + } + + return childList.size(); + } + + /** + * @param i The child to be removed. + */ + public void removeChild( int i ) + { + Xpp3Dom child = childList.remove( i ); + childMap.values().remove( child ); + child.setParent( null ); + } + + /** + * @return The current parent. + */ + public Xpp3Dom getParent() + { + return parent; + } + + /** + * @param parent Set the parent. + */ + public void setParent( Xpp3Dom parent ) + { + this.parent = parent; + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @param childMergeOverride true if child merge will take precedence false otherwise. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive, childMergeOverride ); + } + + /** + * @param dominant The dominant part. + * @param recessive The recessive part. + * @return The merged Xpp3Dom. + */ + public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive ) + { + return Xpp3DomUtils.mergeXpp3Dom( dominant, recessive ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public boolean equals(java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.apache.maven.shared.utils.xml.Xpp3Dom)) { + return false; + } + org.apache.maven.shared.utils.xml.Xpp3Dom dom = ((org.apache.maven.shared.utils.xml.Xpp3Dom) (obj)); + return (((!(name == null ? dom.name != null : !name.equals(dom.name))) && (!(value == null ? dom.value != null : !value.equals(dom.value)))) && (!(!/* NPEX_NULL_EXP */ + attributes.equals(dom.attributes)))) && (!(childList == null ? dom.childList != null : !childList.equals(dom.childList))); +} + + /** {@inheritDoc} */ + public int hashCode() + { + int result = 17; + result = 37 * result + ( name != null ? name.hashCode() : 0 ); + result = 37 * result + ( value != null ? value.hashCode() : 0 ); + result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 ); + result = 37 * result + ( childList != null ? childList.hashCode() : 0 ); + return result; + } + + /** {@inheritDoc} */ + public String toString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + /** + * @return Unescaped string. + */ + public String toUnescapedString() + { + try + { + StringWriter writer = new StringWriter(); + Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this, false ); + return writer.toString(); + } + catch ( final IOException e ) + { + // JDK error in StringWriter. + throw (AssertionError) new AssertionError( "Unexpected IOException from StringWriter." ).initCause( e ); + } + } + + private PrettyPrintXMLWriter getPrettyPrintXMLWriter( StringWriter writer ) + { + return new PrettyPrintXMLWriter( writer, "UTF-8", null ); + } + + /** + * @param str The string to be checked. + * @return true if the string is not empty (length > 0) and not null. + */ + public static boolean isNotEmpty( String str ) + { + return str != null && str.length() > 0; + } + + /** + * @param str The string to be checked. + * @return true if the string is empty or null. + */ + public static boolean isEmpty( String str ) + { + return str == null || str.trim().length() == 0; + } + + /** {@inheritDoc} */ + public Iterator iterator() + { + return getChildrenList().iterator(); + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_352/metadata.json b/Java/maven-shared-utils-Xpp3Dom_352/metadata.json new file mode 100644 index 000000000..1ff200cb2 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_352/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-shared-utils-Xpp3Dom_352", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 348, + "npe_method": "equals", + "deref_field": "attributes", + "npe_class": "Xpp3Dom", + "repo": "maven-shared-utils", + "bug_id": "Xpp3Dom_352" + } +} diff --git a/Java/maven-shared-utils-Xpp3Dom_352/npe.json b/Java/maven-shared-utils-Xpp3Dom_352/npe.json new file mode 100644 index 000000000..5dbf8ad46 --- /dev/null +++ b/Java/maven-shared-utils-Xpp3Dom_352/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/shared/utils/xml/Xpp3Dom.java", + "line": 348, + "npe_method": "equals", + "deref_field": "attributes", + "npe_class": "Xpp3Dom" +} \ No newline at end of file