Skip to content

Commit 032f71f

Browse files
committed
chore: remove uses of commons-collect4
1 parent 22349fd commit 032f71f

11 files changed

Lines changed: 246 additions & 26 deletions

File tree

src/components/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ dependencies {
6767
implementation("org.apache.httpcomponents:httpcore-nio")
6868
implementation("org.jsoup:jsoup")
6969
implementation("net.sf.jtidy:jtidy")
70-
implementation("org.apache.commons:commons-collections4")
7170
implementation("org.apache.commons:commons-math3")
7271
implementation("commons-io:commons-io") {
7372
because("IOUtils")

src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
import javax.swing.tree.TreePath;
6262
import javax.swing.tree.TreeSelectionModel;
6363

64-
import org.apache.commons.collections4.queue.CircularFifoQueue;
6564
import org.apache.jmeter.JMeter;
6665
import org.apache.jmeter.assertions.AssertionResult;
6766
import org.apache.jmeter.gui.GUIMenuSortOrder;
@@ -139,20 +138,16 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
139138
private Object resultsObject = null;
140139
private TreeSelectionEvent lastSelectionEvent;
141140
private JCheckBox autoScrollCB;
142-
private final Queue<SampleResult> buffer;
141+
private final Queue<SampleResult> buffer = new ArrayDeque<>();
142+
private final int maxResults;
143143
private boolean dataChanged;
144144

145145
/**
146146
* Constructor
147147
*/
148148
public ViewResultsFullVisualizer() {
149149
super();
150-
final int maxResults = JMeterUtils.getPropDefault("view.results.tree.max_results", 500);
151-
if (maxResults > 0) {
152-
buffer = new CircularFifoQueue<>(maxResults);
153-
} else {
154-
buffer = new ArrayDeque<>();
155-
}
150+
this.maxResults = JMeterUtils.getPropDefault("view.results.tree.max_results", 500);
156151
init();
157152
new Timer(REFRESH_PERIOD, e -> updateGui()).start();
158153
}
@@ -161,6 +156,9 @@ public ViewResultsFullVisualizer() {
161156
@Override
162157
public void add(final SampleResult sample) {
163158
synchronized (buffer) {
159+
if (maxResults > 0 && buffer.size() >= maxResults) {
160+
buffer.remove();
161+
}
164162
buffer.add(sample);
165163
dataChanged = true;
166164
}

src/core/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ dependencies {
9999
}
100100
implementation("org.jetbrains.lets-plot:lets-plot-batik")
101101
implementation("org.jetbrains.lets-plot:lets-plot-kotlin-jvm")
102-
implementation("org.apache.commons:commons-collections4")
103102
implementation("org.apache.commons:commons-math3") {
104103
because("Mean, DescriptiveStatistics")
105104
}

src/core/src/main/java/org/apache/jmeter/config/Arguments.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
import java.util.List;
2424
import java.util.Map;
2525

26-
import org.apache.commons.collections4.iterators.FilterIterator;
2726
import org.apache.jmeter.testelement.property.CollectionProperty;
2827
import org.apache.jmeter.testelement.property.JMeterProperty;
2928
import org.apache.jmeter.testelement.property.PropertyIterator;
3029
import org.apache.jmeter.testelement.property.TestElementProperty;
3130
import org.apache.jmeter.testelement.schema.PropertiesAccessor;
31+
import org.apache.jorphan.collections.FilterIterator;
3232
import org.apiguardian.api.API;
3333

3434
/**

src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import javax.swing.ScrollPaneConstants;
2929
import javax.swing.Timer;
3030

31-
import org.apache.commons.collections4.queue.CircularFifoQueue;
3231
import org.apache.jmeter.gui.logging.GuiLogEventListener;
3332
import org.apache.jmeter.gui.logging.LogEventObject;
3433
import org.apache.jmeter.gui.util.JSyntaxTextArea;
@@ -57,19 +56,14 @@ public class LoggerPanel extends JPanel implements GuiLogEventListener {
5756
private static final int LOGGER_PANEL_REFRESH_PERIOD =
5857
JMeterUtils.getPropDefault("jmeter.gui.refresh_period", 500); // $NON-NLS-1$
5958

60-
private final Queue<String> events;
59+
private final Queue<String> events = new ArrayDeque<>();
6160

6261
private volatile boolean logChanged = false;
6362

6463
/**
6564
* Pane for display JMeter log file
6665
*/
6766
public LoggerPanel() {
68-
if (LOGGER_PANEL_MAX_LINES > 0) {
69-
events = new CircularFifoQueue<>(LOGGER_PANEL_MAX_LINES);
70-
} else {
71-
events = new ArrayDeque<>();
72-
}
7367
textArea = init();
7468
}
7569

@@ -113,6 +107,9 @@ public void processLogEvent(final LogEventObject logEventObject) {
113107

114108
String logMessage = logEventObject.toString();
115109
synchronized (events) {
110+
if (LOGGER_PANEL_MAX_LINES > 0 && events.size() >= LOGGER_PANEL_MAX_LINES) {
111+
events.remove();
112+
}
116113
events.add(logMessage);
117114
}
118115

src/core/src/main/java/org/apache/jmeter/save/CSVSaveService.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@
3636
import java.util.ArrayList;
3737
import java.util.Arrays;
3838
import java.util.Date;
39+
import java.util.HashMap;
40+
import java.util.LinkedHashMap;
3941
import java.util.List;
42+
import java.util.Map;
4043
import java.util.regex.Matcher;
4144

4245
import javax.swing.table.DefaultTableModel;
4346

44-
import org.apache.commons.collections4.map.LinkedMap;
4547
import org.apache.jmeter.reporters.ResultCollector;
4648
import org.apache.jmeter.samplers.SampleEvent;
4749
import org.apache.jmeter.samplers.SampleResult;
@@ -471,7 +473,8 @@ private static void appendFields(final boolean condition, StringBuilder textBuff
471473
}
472474

473475
// Map header names to set() methods
474-
private static final LinkedMap<String, Functor> headerLabelMethods = new LinkedMap<>();
476+
private static final Map<String, Functor> headerLabelMethods = new LinkedHashMap<>();
477+
private static final Map<String, Integer> headerLabelPositions = new HashMap<>();
475478

476479
// These entries must be in the same order as columns are saved/restored.
477480

@@ -505,6 +508,10 @@ private static void appendFields(final boolean condition, StringBuilder textBuff
505508
headerLabelMethods.put(CSV_HOSTNAME, new Functor("setHostname"));
506509
headerLabelMethods.put(CSV_IDLETIME, new Functor("setIdleTime"));
507510
headerLabelMethods.put(CSV_CONNECT_TIME, new Functor("setConnectTime"));
511+
int pos = 0;
512+
for (String key : headerLabelMethods.keySet()) {
513+
headerLabelPositions.put(key, pos++);
514+
}
508515
}
509516

510517
/**
@@ -605,8 +612,8 @@ private static String[] splitHeader(String headerLine, String delim) {
605612
previous = Integer.MAX_VALUE; // they are always last
606613
continue;
607614
}
608-
int current = headerLabelMethods.indexOf(label);
609-
if (current == -1) {
615+
Integer current = headerLabelPositions.get(label);
616+
if (current == null) {
610617
log.warn("Unknown column name {}", label);
611618
return null; // unknown column name
612619
}

src/dist/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ dependencies {
9292
runtimeOnly("commons-codec:commons-codec") {
9393
because("commons-codec was a dependency in previous JMeter versions, so we keep it for compatibility")
9494
}
95+
runtimeOnly("org.apache.commons:commons-collections4") {
96+
because("commons-collections4 was a dependency in previous JMeter versions, so we keep it for compatibility")
97+
}
9598

9699
binLicense(project(":src:licenses", "binLicense"))
97100
srcLicense(project(":src:licenses", "srcLicense"))

src/jorphan/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ dependencies {
2525
api("org.slf4j:slf4j-api")
2626

2727
implementation("commons-io:commons-io")
28-
implementation("org.apache.commons:commons-collections4")
2928
implementation("org.apache.commons:commons-math3")
3029
implementation("org.apache.commons:commons-text")
3130

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.jorphan.collections
19+
20+
/**
21+
* Iterator that filters elements from another iterator based on a predicate.
22+
*
23+
* This iterator lazily evaluates the predicate as elements are requested,
24+
* only advancing through the source iterator when needed.
25+
*
26+
* @param Element the type of elements returned by this iterator
27+
* @property iterator the source iterator to filter
28+
* @property predicate the predicate that determines which elements to include
29+
*/
30+
public class FilterIterator<Element>(
31+
private val iterator: Iterator<Element>,
32+
private val predicate: (Element) -> Boolean
33+
) : Iterator<Element> {
34+
35+
private var nextElement: Element? = null
36+
private var hasNextElement = false
37+
38+
override fun hasNext(): Boolean {
39+
return hasNextElement || prepareNext()
40+
}
41+
42+
/**
43+
* Prepares the next element by advancing through the iterator
44+
* until an element matching the predicate is found.
45+
* This method is idempotent - calling it multiple times without
46+
* consuming the element has no effect.
47+
*/
48+
private fun prepareNext(): Boolean {
49+
while (iterator.hasNext()) {
50+
val element = iterator.next()
51+
if (predicate(element)) {
52+
nextElement = element
53+
hasNextElement = true
54+
return true
55+
}
56+
}
57+
return false
58+
}
59+
60+
override fun next(): Element {
61+
if (!hasNextElement && !prepareNext()) {
62+
throw NoSuchElementException("No more elements matching the predicate")
63+
}
64+
65+
hasNextElement = false
66+
@Suppress("UNCHECKED_CAST")
67+
val result = nextElement as Element
68+
nextElement = null
69+
return result
70+
}
71+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.jorphan.collections
19+
20+
import org.junit.jupiter.api.Assertions.assertEquals
21+
import org.junit.jupiter.api.Assertions.assertFalse
22+
import org.junit.jupiter.api.Assertions.assertThrows
23+
import org.junit.jupiter.api.Assertions.assertTrue
24+
import org.junit.jupiter.api.Test
25+
26+
class FilterIteratorTest {
27+
28+
private fun <T> assertFilteredResults(collection: Collection<T>, predicate: (T) -> Boolean) {
29+
assertEquals(
30+
collection.filter(predicate).toList().toString(),
31+
FilterIterator(collection.iterator(), predicate).asSequence().toList().toString(),
32+
) {
33+
"input: ${collection.toList()}"
34+
}
35+
}
36+
37+
@Test
38+
fun `filter even numbers from list`() {
39+
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
40+
41+
assertFilteredResults(numbers) { it % 2 == 0 }
42+
}
43+
44+
@Test
45+
fun `filter odd numbers from list`() {
46+
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
47+
48+
assertFilteredResults(numbers) { it % 2 != 0 }
49+
}
50+
51+
@Test
52+
fun `filter strings by length`() {
53+
val words = listOf("a", "ab", "abc", "abcd", "abcde")
54+
assertFilteredResults(words) { it.length > 2 }
55+
}
56+
57+
@Test
58+
fun `filter with no matches returns empty iterator`() {
59+
val numbers = listOf(1, 3, 5, 7, 9)
60+
assertFilteredResults(numbers) { it % 2 == 0 }
61+
}
62+
63+
@Test
64+
fun `filter with all matches returns all elements`() {
65+
val numbers = listOf(2, 4, 6, 8, 10)
66+
assertFilteredResults(numbers) { it % 2 == 0 }
67+
}
68+
69+
@Test
70+
fun `filter empty list returns empty iterator`() {
71+
val empty = emptyList<Int>()
72+
assertFilteredResults(empty) { it > 0 }
73+
}
74+
75+
@Test
76+
fun `filter with null values`() {
77+
val items = listOf("a", null, "b", null, "c")
78+
assertFilteredResults(items) { it != null }
79+
}
80+
81+
@Test
82+
fun `filter with always true predicate`() {
83+
val numbers = listOf(1, 2, 3, 4, 5)
84+
assertFilteredResults(numbers) { true }
85+
}
86+
87+
@Test
88+
fun `filter with always false predicate`() {
89+
val numbers = listOf(1, 2, 3, 4, 5)
90+
assertFilteredResults(numbers) { false }
91+
}
92+
93+
@Test
94+
fun `calling next without hasNext throws exception`() {
95+
val numbers = listOf(1, 3, 5)
96+
val filtered = FilterIterator(numbers.iterator()) { it % 2 == 0 }
97+
98+
assertThrows(NoSuchElementException::class.java) {
99+
filtered.next()
100+
}
101+
}
102+
103+
@Test
104+
fun `calling next after exhausted throws exception`() {
105+
val numbers = listOf(2)
106+
val filtered = FilterIterator(numbers.iterator()) { it % 2 == 0 }
107+
108+
assertTrue(filtered.hasNext())
109+
assertEquals(2, filtered.next())
110+
assertFalse(filtered.hasNext())
111+
112+
assertThrows(NoSuchElementException::class.java) {
113+
filtered.next()
114+
}
115+
}
116+
117+
@Test
118+
fun `multiple calls to hasNext are idempotent`() {
119+
val numbers = listOf(1, 2, 3, 4, 5)
120+
val filtered = FilterIterator(numbers.iterator()) { it % 2 == 0 }
121+
122+
assertTrue(filtered.hasNext())
123+
assertTrue(filtered.hasNext())
124+
assertTrue(filtered.hasNext())
125+
assertEquals(2, filtered.next())
126+
}
127+
128+
@Test
129+
fun `filter chain with multiple predicates`() {
130+
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
131+
132+
// Filter even numbers, then filter those > 5
133+
val filtered1 = FilterIterator(numbers.iterator()) { it % 2 == 0 }
134+
val filtered2 = FilterIterator(filtered1) { it > 5 }
135+
136+
val result = filtered2.asSequence().toList()
137+
138+
assertEquals(listOf(6, 8, 10), result)
139+
}
140+
141+
@Test
142+
fun `filter with single element matching`() {
143+
val numbers = listOf(1, 2, 3)
144+
val filtered = FilterIterator(numbers.iterator()) { it == 2 }
145+
146+
assertTrue(filtered.hasNext())
147+
assertEquals(2, filtered.next())
148+
assertFalse(filtered.hasNext())
149+
}
150+
}

0 commit comments

Comments
 (0)