diff --git a/CVE-2023-2976-pre.patch b/CVE-2023-2976-pre.patch
index 28f161f..6d5288f 100644
--- a/CVE-2023-2976-pre.patch
+++ b/CVE-2023-2976-pre.patch
@@ -10,17 +10,92 @@ RELNOTES=n/a
PiperOrigin-RevId: 526998248
Origin: https://github.com/google/guava/commit/364aed58e083e305f3f9ffc5bcacf9536f2472ab
-
---
- .../common/io/FilesCreateTempDirTest.java | 37 +++++++++++++++++
- .../test/com/google/common/io/FilesTest.java | 15 +++----
- .../common/annotations/J2ktIncompatible.java | 31 ++++++++++++++
- .../io/ElementTypesAreNonnullByDefault.java | 41 +++++++++++++++++++
- 4 files changed, 115 insertions(+), 9 deletions(-)
+ .../common/io/FilesCreateTempDirTest.java | 38 +++++++++++++++++++
+ .../test/com/google/common/io/FilesTest.java | 15 +++-----
+ .../common/io/FilesCreateTempDirTest.java | 37 ++++++++++++++++++
+ .../test/com/google/common/io/FilesTest.java | 15 +++-----
+ 4 files changed, 87 insertions(+), 18 deletions(-)
+ create mode 100644 android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java
create mode 100644 guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java
- create mode 100644 guava/src/com/google/common/annotations/J2ktIncompatible.java
- create mode 100644 guava/src/com/google/common/io/ElementTypesAreNonnullByDefault.java
+diff --git a/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java b/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java
+new file mode 100644
+index 0000000..109342a
+--- /dev/null
++++ b/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java
+@@ -0,0 +1,38 @@
++/*
++ * Copyright (C) 2007 The Guava Authors
++ *
++ * Licensed under the Apache License, Version 2.0 (the "License");
++ * you may not use this file except in compliance with the License.
++ * You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++package com.google.common.io;
++
++import static com.google.common.truth.Truth.assertThat;
++
++import java.io.File;
++import junit.framework.TestCase;
++
++/**
++ * Unit test for {@link Files#createTempDir}.
++ *
++ * @author Chris Nokleberg
++ */
++
++public class FilesCreateTempDirTest extends TestCase {
++ public void testCreateTempDir() {
++ File temp = Files.createTempDir();
++ assertTrue(temp.exists());
++ assertTrue(temp.isDirectory());
++ assertThat(temp.listFiles()).isEmpty();
++ assertTrue(temp.delete());
++ }
++}
+diff --git a/android/guava-tests/test/com/google/common/io/FilesTest.java b/android/guava-tests/test/com/google/common/io/FilesTest.java
+index 8446da1..338059e 100644
+--- a/android/guava-tests/test/com/google/common/io/FilesTest.java
++++ b/android/guava-tests/test/com/google/common/io/FilesTest.java
+@@ -42,7 +42,12 @@ import junit.framework.TestSuite;
+ /**
+ * Unit test for {@link Files}.
+ *
+- *
Note: {@link Files#fileTraverser()} is tested in {@link FilesFileTraverserTest}.
++ *
Temporary files created by this stream may live in the local filesystem until either:
*
- * @author Chris Nokleberg
-@@ -70,6 +80,7 @@ public final class FileBackedOutputStream extends OutputStream {
+ *
+@@ -57,7 +66,6 @@ public final class FileBackedOutputStream extends OutputStream {
+ private final int fileThreshold;
+ private final boolean resetOnFinalize;
+ private final ByteSource source;
+- @CheckForNull private final File parentDirectory;
+
+ @GuardedBy("this")
+ private OutputStream out;
+@@ -93,6 +101,7 @@ public final class FileBackedOutputStream extends OutputStream {
* {@link ByteSource} returned by {@link #asByteSource} is finalized.
*
* @param fileThreshold the number of bytes before the stream should switch to buffering to a file
@@ -188,33 +194,41 @@ index b002561..23d427c 100644
*/
public FileBackedOutputStream(int fileThreshold) {
this(fileThreshold, false);
-@@ -82,8 +93,11 @@ public final class FileBackedOutputStream extends OutputStream {
+@@ -105,16 +114,13 @@ public final class FileBackedOutputStream extends OutputStream {
* @param fileThreshold the number of bytes before the stream should switch to buffering to a file
* @param resetOnFinalize if true, the {@link #reset} method will be called when the {@link
- * ByteSource} returned by {@link #asByteSource} is finalized
+ * ByteSource} returned by {@link #asByteSource} is finalized.
+ * @throws IllegalArgumentException if {@code fileThreshold} is negative
*/
public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) {
+- this(fileThreshold, resetOnFinalize, null);
+- }
+-
+- private FileBackedOutputStream(
+- int fileThreshold, boolean resetOnFinalize, @CheckForNull File parentDirectory) {
+ checkArgument(
+ fileThreshold >= 0, "fileThreshold must be non-negative, but was %s", fileThreshold);
this.fileThreshold = fileThreshold;
this.resetOnFinalize = resetOnFinalize;
+- this.parentDirectory = parentDirectory;
memory = new MemoryOutput();
-@@ -193,7 +207,7 @@ public final class FileBackedOutputStream extends OutputStream {
- */
+ out = memory;
+
+@@ -225,7 +231,7 @@ public final class FileBackedOutputStream extends OutputStream {
+ @GuardedBy("this")
private void update(int len) throws IOException {
- if (file == null && (memory.getCount() + len > fileThreshold)) {
-- File temp = File.createTempFile("FileBackedOutputStream", null);
+ if (memory != null && (memory.getCount() + len > fileThreshold)) {
+- File temp = File.createTempFile("FileBackedOutputStream", null, parentDirectory);
+ File temp = TempFileCreator.INSTANCE.createTempFile("FileBackedOutputStream");
if (resetOnFinalize) {
// Finalizers are not guaranteed to be called on system shutdown;
// this is insurance.
diff --git a/guava/src/com/google/common/io/Files.java b/guava/src/com/google/common/io/Files.java
-index b0c4390..a2ef1d0 100644
+index ba5528f..2bc67ab 100644
--- a/guava/src/com/google/common/io/Files.java
+++ b/guava/src/com/google/common/io/Files.java
-@@ -67,9 +67,6 @@ import java.util.List;
- @GwtIncompatible
+@@ -70,9 +70,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
+ @ElementTypesAreNonnullByDefault
public final class Files {
- /** Maximum loop count when creating temp directories. */
@@ -223,7 +237,7 @@ index b0c4390..a2ef1d0 100644
private Files() {}
/**
-@@ -380,17 +377,19 @@ public final class Files {
+@@ -394,17 +391,19 @@ public final class Files {
* Atomically creates a new directory somewhere beneath the system's temporary directory (as
* defined by the {@code java.io.tmpdir} system property), and returns its name.
*
@@ -248,7 +262,7 @@ index b0c4390..a2ef1d0 100644
* This method assumes that the temporary volume is writable, has free inodes and free blocks,
* and that it will not be called thousands of times per second.
*
-@@ -399,33 +398,24 @@ public final class Files {
+@@ -413,35 +412,25 @@ public final class Files {
*
* @return the newly-created directory
* @throws IllegalStateException if the directory could not be created
@@ -270,9 +284,11 @@ index b0c4390..a2ef1d0 100644
+ * PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"))} to your
+ * call to {@code createTempDirectory}.
*/
+ @Beta
@Deprecated
public static File createTempDir() {
- File baseDir = new File(System.getProperty("java.io.tmpdir"));
+- @SuppressWarnings("GoodTime") // reading system time without TimeSource
- String baseName = System.currentTimeMillis() + "-";
-
- for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
@@ -332,7 +348,7 @@ index 0000000..b1b8e10
+@interface IgnoreJRERequirement {}
diff --git a/guava/src/com/google/common/io/TempFileCreator.java b/guava/src/com/google/common/io/TempFileCreator.java
new file mode 100644
-index 0000000..103427a
+index 0000000..a28a0af
--- /dev/null
+++ b/guava/src/com/google/common/io/TempFileCreator.java
@@ -0,0 +1,176 @@
@@ -397,9 +413,9 @@ index 0000000..103427a
+ }
+
+ try {
-+ int version = ((Integer) Class.forName("android.os.Build$VERSION").getField("SDK_INT").get(null)).intValue();
++ int version = (int) Class.forName("android.os.Build$VERSION").getField("SDK_INT").get(null);
+ int jellyBean =
-+ ((Integer) Class.forName("android.os.Build$VERSION_CODES").getField("JELLY_BEAN").get(null)).intValue();
++ (int) Class.forName("android.os.Build$VERSION_CODES").getField("JELLY_BEAN").get(null);
+ /*
+ * I assume that this check can't fail because JELLY_BEAN will be present only if we're
+ * running under Jelly Bean or higher. But it seems safest to check.
@@ -513,5 +529,5 @@ index 0000000..103427a
+ private TempFileCreator() {}
+}
--
-2.33.0
+2.41.0
diff --git a/guava-31.1.tar.gz b/guava-31.1.tar.gz
new file mode 100644
index 0000000..9a5f498
Binary files /dev/null and b/guava-31.1.tar.gz differ
diff --git a/guava.spec b/guava.spec
index 2285999..3445818 100644
--- a/guava.spec
+++ b/guava.spec
@@ -1,18 +1,23 @@
Name: guava
-Version: 25.0
-Release: 6
+Version: 31.1
+Release: 1
Summary: Google Core Libraries for Java
License: ASL 2.0 and CC0
URL: https://github.com/google/guava
BuildArch: noarch
-Source0: https://github.com/google/guava/archive/v%{version}.tar.gz
-Patch0000: CVE-2020-8908.patch
+Source0: https://github.com/google/guava/archive/v%{version}/guava-%{version}.tar.gz
+Source1: java_remove_symbols.lua
+Source2: java_symbols_lib.lua
+
Patch0001: CVE-2023-2976-pre.patch
Patch0002: CVE-2023-2976.patch
BuildRequires: maven-local mvn(com.google.code.findbugs:jsr305) mvn(junit:junit)
BuildRequires: mvn(org.apache.felix:maven-bundle-plugin) mvn(org.sonatype.oss:oss-parent:pom:)
+BuildRequires: java-17-openjdk-devel
+Requires: java-17-openjdk
+
%description
Guava is a set of core Java libraries from Google that includes new collection
@@ -40,9 +45,15 @@ To facilitate unit testing, guava-testlib provides additional features
find . -name '*.jar' -delete
+%pom_remove_parent guava-bom
+
%pom_disable_module guava-gwt
%pom_disable_module guava-tests
+%pom_xpath_inject pom:modules "futures/failureaccess"
+%pom_xpath_inject pom:parent "../.." futures/failureaccess
+%pom_xpath_set pom:parent/pom:version %{version}-jre futures/failureaccess
+
%pom_remove_plugin -r :animal-sniffer-maven-plugin
%pom_remove_plugin :maven-dependency-plugin guava
@@ -53,27 +64,28 @@ find . -name '*.jar' -delete
%pom_remove_plugin -r :maven-javadoc-plugin
%pom_xpath_inject /pom:project/pom:build/pom:plugins/pom:plugin/pom:configuration/pom:instructions "<_nouses>true" guava/pom.xml
-
-%pom_remove_dep -r :animal-sniffer-annotations
+
%pom_remove_dep -r :error_prone_annotations
%pom_remove_dep -r :j2objc-annotations
%pom_remove_dep -r org.checkerframework:
+%pom_remove_dep -r :listenablefuture
-annotations=$(
- find -name '*.java' \
- | xargs fgrep -h \
- -e 'import com.google.j2objc.annotations' \
- -e 'import com.google.errorprone.annotation' \
- -e 'import org.codehaus.mojo.animal_sniffer' \
- -e 'import org.checkerframework' \
- | sort -u \
- | sed 's/.*\.\([^.]*\);/\1/' \
- | paste -sd\|
-)
-find -name '*.java' | xargs sed -ri \
- "s/^import .*\.($annotations);//;s/@($annotations)"'\>\s*(\((("[^"]*")|([^)]*))\))?//g'
+export LUA_PATH=%{SOURCE2}
+
+/usr/bin/lua %{SOURCE1} -a guava guava-testlib -p org[.]checkerframework[.]
+/usr/bin/lua %{SOURCE1} -a guava guava-testlib -p com[.]google[.]common[.]annotations[.]
+/usr/bin/lua %{SOURCE1} -a guava guava-testlib -p com[.]google[.]errorprone[.]annotations[.]
+/usr/bin/lua %{SOURCE1} -a guava guava-testlib -p com[.]google[.]j2objc[.]annotations[.]
+
+
+%mvn_package "com.google.guava:failureaccess" guava
+
+%mvn_package "com.google.guava:guava-bom" __noinstall
%build
+export JAVA_HOME=%{_jvmdir}/java-17-openjdk
+export CFLAGS="${RPM_OPT_FLAGS}"
+export CXXFLAGS="${RPM_OPT_FLAGS}"
%mvn_build -s -f
%install
@@ -89,6 +101,9 @@ find -name '*.java' | xargs sed -ri \
%files testlib -f .mfiles-guava-testlib
%changelog
+* Wed Sep 27 2023 chenchen - 31.1-1
+- Upgrade to version 31.1
+
* Tue Jul 04 2023 wangkai <13474090681@163.com> - 25.0-6
- Fix CVE-2023-2976
diff --git a/guava.yaml b/guava.yaml
new file mode 100644
index 0000000..d1416be
--- /dev/null
+++ b/guava.yaml
@@ -0,0 +1,4 @@
+version_control: github
+src_repo: google/guava
+tag_prefix: "^"
+seperator: "."
diff --git a/java_remove_symbols.lua b/java_remove_symbols.lua
new file mode 100644
index 0000000..1d3cc1b
--- /dev/null
+++ b/java_remove_symbols.lua
@@ -0,0 +1,16 @@
+#!/usr/bin/lua
+
+local lib = require("java_symbols_lib")
+
+local help_usage = [[
+Usage: java_remove_symbols.lua [-a] [list of file paths]... [-n ...] [-p ...]
+ -a Also remove annotations used in code
+ -n List of simple (not fully-qualified) class names
+ -p List of patterns to match names used in code]]
+
+if #arg == 0 or arg[1] == "-h" or arg[1] == "--help" then
+ print(help_usage)
+ return
+end
+
+lib.main(arg)
diff --git a/java_symbols_lib.lua b/java_symbols_lib.lua
new file mode 100644
index 0000000..c18b72a
--- /dev/null
+++ b/java_symbols_lib.lua
@@ -0,0 +1,381 @@
+--
+-- Copyright (c) 2022, Red Hat, Inc.
+-- All rights reserved.
+--
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions
+-- are met:
+--
+-- 1. Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- 2. Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the
+-- distribution.
+-- 3. Neither the name of Red Hat nor the names of its
+-- contributors may be used to endorse or promote products derived
+-- from this software without specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+-- Author: Marián Konček
+--------------------------------------------------------------------------------
+
+--! Iterates over @p string starting from @p position until a character
+--! is found which is neither a whitespace nor a Java comment
+--! @return The position of the first non-whitespace non-comment charater or
+--! #string + 1 if none is found
+local function ignore_whitespace_comments(string, position)
+ while position <= #string do
+ local result = position
+ position = string.find(string, "[^%s]", position)
+ if position == nil then
+ return #string + 1
+ end
+ if string.sub(string, position, position + 1) == "//" then
+ position = string.find(string, "\n", position + 2)
+ if position == nil then
+ return #string + 1
+ end
+ position = position + 1
+ elseif string.sub(string, position, position + 1) == "/*" then
+ _, position = string.find(string, "*/", position + 2)
+ position = position + 1
+ end
+ if result == position then
+ return result
+ end
+ end
+
+ return position
+end
+
+--! Finds the next uncommented symbol and returns it and an index pointing past
+--! it. The symbol is wither a sequence of alphanumeric characters or a single
+--! non-aphanumeric character or an empty string if the end has been reached.
+local function next_symbol(string, position)
+ position = position or 1
+ local next_position = position
+ local word_end = #string
+
+ if position <= #string then
+ next_position = ignore_whitespace_comments(string, position)
+ if next_position <= #string then
+ _, word_end = string.find(string, "%w*", next_position)
+ if word_end < next_position then
+ word_end = next_position
+ end
+ end
+ end
+
+ return string.sub(string, next_position, word_end), word_end + 1
+end
+
+--! Searches within @p string starting from @p position to find a string
+--! @param token which is present in the source code neither inside a comment
+--! nor inside a string nor inside a character literal. Special case when
+--! @p token == ')', this function counts opening and closing parentheses
+--! and returns the first parethesis outside.
+--! @param alphanumeric If true, considers only tokens that are surrounded by
+--! whitespace, comments or are at the boundaries of @p string
+--! @return The starting index or #string + 1.
+local function find_token(string, token, position, alphanumeric, stack)
+ position = position or 1
+ alphanumeric = alphanumeric or false
+ stack = stack or 0
+
+ while position <= #string do
+ position = ignore_whitespace_comments(string, position)
+ if false then
+ elseif string.sub(string, position, position) == "\'" then
+ if string.sub(string, position, position + 3) == "\'\\\'\'" then
+ position = position + 3
+ else
+ position = position + 1
+ while string.sub(string, position, position) ~= "\'" do
+ position = position + 1
+ end
+ end
+ elseif (token ~= ")" or stack == 0) and string.sub(string, position, position + #token - 1) == token
+ and not (alphanumeric
+ -- NOTE string.sub outsde of valid range returns an empty string
+ and (string.find(string.sub(string, position - 1, position - 1), "[%w_]")
+ or string.find(string.sub(string, position + #token, position + #token), "[%w_]"))) then
+ return position
+ elseif string.sub(string, position, position) == "\"" then
+ position = position + 1
+ while string.sub(string, position, position) ~= "\"" do
+ if string.sub(string, position, position + 1) == "\\\\" then
+ position = position + 2
+ elseif string.sub(string, position, position + 1) == "\\\"" then
+ position = position + 2
+ else
+ position = position + 1
+ end
+ end
+ elseif stack ~= 0 and string.sub(string, position, position) == ")" then
+ stack = stack - 1
+ elseif string.sub(string, position, position) == "(" then
+ stack = stack + 1
+ end
+
+ position = position + 1
+ end
+
+ return #string + 1
+end
+
+--! @return A triple of values:
+--! 1: The index of the starting '@' symbol or #string + 1
+--! 2: The index pointing past the annotation name or #string + 1
+--! 3: The extracted annotation name with all whitespace characters and comments
+--! stripped
+local function next_annotation(string, position)
+ local result = ""
+ local end_pos = #string + 1
+ position = find_token(string, "@", position)
+ local expecting_dot = false
+
+ if position <= #string then
+ local symbol
+ symbol, end_pos = next_symbol(string, position + 1)
+ local new_end_pos = end_pos
+
+ while true do
+ if expecting_dot and symbol ~= "." then
+ if symbol == "(" then
+ end_pos = find_token(string, ")", new_end_pos) + 1
+ end
+ break
+ end
+
+ result = result..symbol
+ expecting_dot = not expecting_dot
+ end_pos = new_end_pos
+ symbol, new_end_pos = next_symbol(string, new_end_pos)
+ end
+ end
+
+ return position, end_pos, result
+end
+
+--! @param name Class name found in code, may be fully-qualified or simple.
+--! @param patterns A table of patterns.
+--! @param names A table of names, these are matched exactly by the simple class
+--! names.
+--! @param imported_names Names that have their import declarations removed. In
+--! this case, we match the full name and this will only remove names that are
+--! used in their simple form in code (therefore they refer to the removed
+--! import declaration. If a name is used in its fully-qualified form in the
+--! code, it will be catched by the other matchers.
+--! @return The simple class name.
+local function name_matches(name, patterns, names, imported_names)
+ local class_name = select(3, string.find(name, ".*[.](.*)")) or name
+
+ if names[class_name] then
+ return class_name
+ end
+
+ if imported_names[name] then
+ return class_name
+ end
+
+ for _, pattern in ipairs(patterns) do
+ if string.find(name, pattern) then
+ return class_name
+ end
+ end
+
+ return nil
+end
+
+local function remove_imports(content, patterns, names)
+ local position = 1
+ local result = ""
+ local removed_classes = {}
+ names = names or {}
+
+ while position <= #content do
+ local next_position = find_token(content, "import", position, true)
+ local copy_end = #content + 1
+
+ if next_position <= #content then
+ local import_name = ""
+ local symbol, end_pos
+ symbol, end_pos = next_symbol(content, next_position + 6)
+ local names_passed = names
+
+ if symbol == "static" then
+ names_passed = {}
+ symbol, end_pos = next_symbol(content, end_pos)
+ end
+ while symbol ~= ";" do
+ import_name = import_name..symbol
+ symbol, end_pos = next_symbol(content, end_pos)
+ end
+ _, end_pos = string.find(content, "%s-\n?", end_pos)
+ copy_end = end_pos + 1
+
+ local class_name = name_matches(import_name, patterns, names_passed, {})
+
+ if class_name then
+ copy_end = next_position
+ if class_name ~= "*" then
+ removed_classes[class_name] = true
+ end
+ end
+
+ next_position = end_pos + 1
+ end
+
+ result = result..string.sub(content, position, copy_end - 1)
+ position = next_position
+ end
+
+ return result, removed_classes
+end
+
+local function remove_annotations(content, patterns, names, imported_names)
+ local position = 1
+ local result = ""
+ names = names or {}
+ imported_names = imported_names or {}
+
+ while position <= #content do
+ local an_pos, an_end, annotation_name = next_annotation(content, position)
+ local next_position = #content + 1
+ local copy_end = #content + 1
+
+ if an_pos <= #content then
+ copy_end = an_end
+ next_position = an_end
+ if name_matches(annotation_name, patterns, names, imported_names) then
+ copy_end = an_pos
+ next_position = string.find(content, "[^%s]", next_position) or next_position
+ end
+ end
+
+ result = result..string.sub(content, position, copy_end - 1)
+ position = next_position
+ end
+
+ return result
+end
+
+local function handle_file(filename, parameters)
+ local patterns = parameters["patterns"]
+ local names = parameters["names"]
+ local file = io.open(filename, "r")
+ local original_content = file:read("*a")
+ file:close()
+ local content = original_content
+ local content, removed_classes = remove_imports(content, patterns, names)
+
+ if parameters["-a"] then
+ -- a new table of patterns + class names of fully-qualified imports
+ -- which were removed
+ content = remove_annotations(content, patterns, names, removed_classes)
+ end
+
+ if not parameters["--dry-run"] and #content < #original_content then
+ print("Removing symbols from file: "..filename)
+ local file = io.open(filename, "w")
+ file:write(content)
+ file:flush()
+ file:close()
+ end
+
+ return content
+end
+
+local function parse_arguments(args, no_argument_flags)
+ local result = {}
+ local last_flag = nil
+
+ for _, value in ipairs(args) do
+ if string.find(value, "^[-][%w]$") or string.find(value, "^[-][-].+$") then
+ result[value] = {}
+ last_flag = value
+ if no_argument_flags[value] then
+ last_flag = nil
+ end
+ elseif value == "--" then
+ last_flag = nil
+ elseif last_flag then
+ table.insert(result[last_flag], value)
+ else
+ table.insert(result, value)
+ end
+ end
+
+ return result
+end
+
+--! The patterns table is used in two ways simultaneously:
+--! 1. as a list of patterns
+--! 2. as a dictionary of simple class names
+local function interpret_args(args_table)
+ local files = {}
+ local names = {}
+ local patterns = args_table["-p"] or {}
+
+ for _, class_name in ipairs(args_table["-n"] or {}) do
+ names[class_name] = true
+ end
+
+ if #args_table == 0 then
+ table.insert(args_table, ".")
+ end
+
+ for _, root in ipairs(args_table) do
+ local popen = io.popen("find '"..root.."' -type f -name '*.java'")
+ for line in popen:lines() do
+ table.insert(files, line)
+ end
+ popen:close()
+ end
+
+ return files, {
+ patterns = patterns,
+ names = names,
+ ["-a"] = args_table["-a"],
+ ["--dry-run"] = args_table["--dry-run"],
+ }
+end
+
+local function main(args)
+ local files, parameters = interpret_args(parse_arguments(args, {["-a"] = true, ["--dry-run"] = true}))
+ local is_dry_run = (parameters["--dry-run"] ~= nil)
+ local result = ""
+
+ for _, filename in ipairs(files) do
+ single_result = handle_file(filename, parameters)
+ if is_dry_run then
+ result = result..single_result
+ end
+ end
+
+ return result
+end
+
+return {
+ main = main,
+
+ -- Used for testing
+ ignore_whitespace_comments = ignore_whitespace_comments,
+ next_symbol = next_symbol,
+ find_token = find_token,
+ next_annotation = next_annotation,
+ remove_imports = remove_imports,
+ remove_annotations = remove_annotations,
+}
diff --git a/v25.0.tar.gz b/v25.0.tar.gz
deleted file mode 100644
index cd81ecd..0000000
Binary files a/v25.0.tar.gz and /dev/null differ