You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by fs...@apache.org on 2021/01/26 17:38:48 UTC

[jmeter] 02/02: Sorting in View Results in Table is not correct

This is an automated email from the ASF dual-hosted git repository.

fschumacher pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git

commit 6e7b3702cac8808937df56409393f42ee9b60e67
Author: Felix Schumacher <fe...@internetallee.de>
AuthorDate: Sat Jan 2 22:36:42 2021 +0100

    Sorting in View Results in Table is not correct
    
    All string columns will get sorted by human understandable alpha numeric sort
    
    Bugzilla Id: 63061
---
 .../apache/jmeter/visualizers/TableVisualizer.java | 11 ++++-
 ...Comparator.java => AlphaNumericComparator.java} | 48 +++++++++++++--------
 .../jorphan/util/AlphaNumericKeyComparator.java    | 50 ++--------------------
 .../util/TestAlphaNumericKeyComparator.java        | 18 +++++++-
 xdocs/changes.xml                                  |  1 +
 5 files changed, 60 insertions(+), 68 deletions(-)

diff --git a/src/components/src/main/java/org/apache/jmeter/visualizers/TableVisualizer.java b/src/components/src/main/java/org/apache/jmeter/visualizers/TableVisualizer.java
index 45afebf..8f3bbbc 100644
--- a/src/components/src/main/java/org/apache/jmeter/visualizers/TableVisualizer.java
+++ b/src/components/src/main/java/org/apache/jmeter/visualizers/TableVisualizer.java
@@ -54,6 +54,7 @@ import org.apache.jorphan.gui.RendererUtils;
 import org.apache.jorphan.gui.RightAlignRenderer;
 import org.apache.jorphan.gui.layout.VerticalLayout;
 import org.apache.jorphan.reflect.Functor;
+import org.apache.jorphan.util.AlphaNumericComparator;
 
 /**
  * This class implements a statistical analyser that calculates both the average
@@ -226,7 +227,7 @@ public class TableVisualizer extends AbstractVisualizer implements Clearable {
 
         // Set up the table itself
         table = new JTable(model);
-        table.setRowSorter(new ObjectTableSorter(model).setValueComparator(5,
+        final ObjectTableSorter rowSorter = new ObjectTableSorter(model).setValueComparator(5,
                 Comparator.nullsFirst(
                         (ImageIcon o1, ImageIcon o2) -> {
                             if (o1 == o2) {
@@ -239,7 +240,13 @@ public class TableVisualizer extends AbstractVisualizer implements Clearable {
                                 return 1;
                             }
                             throw new IllegalArgumentException("Only success and failure images can be compared");
-                        })));
+                        }));
+        for (int i=0; i<model.getColumnCount(); i++) {
+            if (model.getColumnClass(i).equals(String.class)) {
+                rowSorter.setValueComparator(i, new AlphaNumericComparator<Object>(o -> o.toString()));
+            }
+        }
+        table.setRowSorter(rowSorter);
         JMeterUtils.applyHiDPI(table);
         HeaderAsPropertyRendererWrapper.setupDefaultRenderer(table);
         RendererUtils.applyRenderers(table, RENDERERS);
diff --git a/src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericKeyComparator.java b/src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericComparator.java
similarity index 63%
copy from src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericKeyComparator.java
copy to src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericComparator.java
index 9a610eb..9c56ae2 100644
--- a/src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericKeyComparator.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericComparator.java
@@ -16,24 +16,28 @@
  */
 package org.apache.jorphan.util;
 
-import java.math.BigInteger;
 import java.util.Comparator;
-import java.util.Map;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
- * Comparator for {@link Map.Entry} Objects, that compares based on their keys only. The keys
+ * Comparator for Objects, that compares based on their <em>converted</em> values. The objects will be
+ * converted to a String value using the given {@link Function}. That value
  * will be compared in a human readable fashion by trying to parse numbers that appear in
  * the keys as integers and compare those, too.<p>
  * Heavily influenced by https://codereview.stackexchange.com/questions/37192/number-aware-string-sorting-with-comparator
  */
-public class AlphaNumericKeyComparator implements Comparator<Map.Entry<Object, Object>> {
-    
-    public static final AlphaNumericKeyComparator INSTANCE = new AlphaNumericKeyComparator();
-    
-    private AlphaNumericKeyComparator() {
-        // don't instantiate this class on your own.
+public class AlphaNumericComparator<T> implements Comparator<T> {
+
+    private Function<T, String> converter;
+
+    /**
+     * Constructs a comparator with a converter function
+     * @param converter that generates a String value from the arguments given to {@link Comparator#compare(Object, Object)}
+     */
+    public AlphaNumericComparator(Function<T, String> converter) {
+        this.converter = converter;
     }
 
     private static final Pattern parts = Pattern.compile("(\\D*)(\\d*)");
@@ -41,9 +45,9 @@ public class AlphaNumericKeyComparator implements Comparator<Map.Entry<Object, O
     private static final int NUM_PART = 2;
 
     @Override
-    public int compare(Map.Entry<Object, Object> o1, Map.Entry<Object, Object> o2) {
-        Matcher m1 = parts.matcher(o1.getKey().toString());
-        Matcher m2 = parts.matcher(o2.getKey().toString());
+    public int compare(T o1, T o2) {
+        Matcher m1 = parts.matcher(converter.apply(o1));
+        Matcher m2 = parts.matcher(converter.apply(o2));
 
         while (m1.find() && m2.find()) {
             int compareCharGroup = m1.group(ALPHA_PART).compareTo(m2.group(ALPHA_PART));
@@ -60,17 +64,17 @@ public class AlphaNumericKeyComparator implements Comparator<Map.Entry<Object, O
             } else if (numberPart2.isEmpty()) {
                 return 1;
             }
-            int lengthNumber1 = numberPart1.length();
-            int lengthNumber2 = numberPart2.length();
+            String nonZeroNumberPart1 = trimLeadingZeroes(numberPart1);
+            String nonZeroNumberPart2 = trimLeadingZeroes(numberPart2);
+            int lengthNumber1 = nonZeroNumberPart1.length();
+            int lengthNumber2 = nonZeroNumberPart2.length();
             if (lengthNumber1 != lengthNumber2) {
                 if (lengthNumber1 < lengthNumber2) {
                     return -1;
                 }
                 return 1;
             }
-            BigInteger i1 = new BigInteger(numberPart1);
-            BigInteger i2 = new BigInteger(numberPart2);
-            int compareNumber = i1.compareTo(i2);
+            int compareNumber = nonZeroNumberPart1.compareTo(nonZeroNumberPart2);
             if (compareNumber != 0) {
                 return compareNumber;
             }
@@ -84,4 +88,14 @@ public class AlphaNumericKeyComparator implements Comparator<Map.Entry<Object, O
         return 1;
     }
 
+    private String trimLeadingZeroes(String numberPart) {
+        int length = numberPart.length();
+        for (int i = 0; i < length; i++) {
+            if (numberPart.charAt(i) != '0') {
+                return numberPart.substring(i);
+            }
+        }
+        return "";
+    }
+
 }
diff --git a/src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericKeyComparator.java b/src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericKeyComparator.java
index 9a610eb..3ed5e54 100644
--- a/src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericKeyComparator.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/util/AlphaNumericKeyComparator.java
@@ -16,11 +16,8 @@
  */
 package org.apache.jorphan.util;
 
-import java.math.BigInteger;
 import java.util.Comparator;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Comparator for {@link Map.Entry} Objects, that compares based on their keys only. The keys
@@ -31,57 +28,16 @@ import java.util.regex.Pattern;
 public class AlphaNumericKeyComparator implements Comparator<Map.Entry<Object, Object>> {
     
     public static final AlphaNumericKeyComparator INSTANCE = new AlphaNumericKeyComparator();
+    private AlphaNumericComparator<Map.Entry<Object, Object>> comparator;
     
     private AlphaNumericKeyComparator() {
         // don't instantiate this class on your own.
+        this.comparator = new AlphaNumericComparator<Map.Entry<Object, Object>>(e -> e.getKey().toString());
     }
 
-    private static final Pattern parts = Pattern.compile("(\\D*)(\\d*)");
-    private static final int ALPHA_PART = 1;
-    private static final int NUM_PART = 2;
-
     @Override
     public int compare(Map.Entry<Object, Object> o1, Map.Entry<Object, Object> o2) {
-        Matcher m1 = parts.matcher(o1.getKey().toString());
-        Matcher m2 = parts.matcher(o2.getKey().toString());
-
-        while (m1.find() && m2.find()) {
-            int compareCharGroup = m1.group(ALPHA_PART).compareTo(m2.group(ALPHA_PART));
-            if (compareCharGroup != 0) {
-                return compareCharGroup;
-            }
-            String numberPart1 = m1.group(NUM_PART);
-            String numberPart2 = m2.group(NUM_PART);
-            if (numberPart1.isEmpty()) {
-                if (numberPart2.isEmpty()) {
-                    return 0;
-                }
-                return -1;
-            } else if (numberPart2.isEmpty()) {
-                return 1;
-            }
-            int lengthNumber1 = numberPart1.length();
-            int lengthNumber2 = numberPart2.length();
-            if (lengthNumber1 != lengthNumber2) {
-                if (lengthNumber1 < lengthNumber2) {
-                    return -1;
-                }
-                return 1;
-            }
-            BigInteger i1 = new BigInteger(numberPart1);
-            BigInteger i2 = new BigInteger(numberPart2);
-            int compareNumber = i1.compareTo(i2);
-            if (compareNumber != 0) {
-                return compareNumber;
-            }
-        }
-        if (m1.hitEnd() && m2.hitEnd()) {
-            return 0;
-        }
-        if (m1.hitEnd()) {
-            return -1;
-        }
-        return 1;
+        return this.comparator.compare(o1, o2);
     }
 
 }
diff --git a/src/jorphan/src/test/java/org/apache/jorphan/util/TestAlphaNumericKeyComparator.java b/src/jorphan/src/test/java/org/apache/jorphan/util/TestAlphaNumericKeyComparator.java
index 4f19a41..a626d3c 100644
--- a/src/jorphan/src/test/java/org/apache/jorphan/util/TestAlphaNumericKeyComparator.java
+++ b/src/jorphan/src/test/java/org/apache/jorphan/util/TestAlphaNumericKeyComparator.java
@@ -13,16 +13,28 @@ class TestAlphaNumericKeyComparator {
 
     @ParameterizedTest
     @ValueSource(strings = { "abc", "", "var_123", "434", "_" })
-    void testComparatorWithEqualKeys(String candidate) {
+    void testComparatorWithSameKeys(String candidate) {
         Comparator<Map.Entry<Object, Object>> comparator = AlphaNumericKeyComparator.INSTANCE;
         assertEquals(0, comparator.compare(entry(candidate), entry(candidate)));
     }
 
     @ParameterizedTest
+    @CsvSource({ "abc-001, abc-1", "007, 7", "0000, 0", "abc|000, abc|0" })
+    void testComparatorWithEquivalentKeys(String left, String right) {
+        Comparator<Map.Entry<Object, Object>> comparator = AlphaNumericKeyComparator.INSTANCE;
+        assertEquals(0, comparator.compare(entry(left), entry(right)));
+        assertEquals(0, comparator.compare(entry(right), entry(left)));
+    }
+
+    @ParameterizedTest
     @CsvSource({
         "a,                    1",
+        "something-0001,       999999999999999999999999999999999999999999999999999999999999999999999999999999",
+        "abc[23],              abc[2]",
         "a10,                  a1",
         "a2,                   a1",
+        "2,                    01",
+        "0010,                 000005",
         "a20,                  a10",
         "a10,                  a2",
         "z,                    10000",
@@ -32,7 +44,9 @@ class TestAlphaNumericKeyComparator {
         "abc,                  ''",
         "'abc.,${something}1', 'abc.,${something}'",
         "number1,              number",
-        "789b,                 789"
+        "789b,                 789",
+        "0xcafebabe,           0x8664",
+        "abc_0000,             abc_"
         })
     void testComparatorDifferentKeys(String higher, String lower) {
         Comparator<Map.Entry<Object, Object>> comparator = AlphaNumericKeyComparator.INSTANCE;
diff --git a/xdocs/changes.xml b/xdocs/changes.xml
index 64abc0f..2d0c1fd 100644
--- a/xdocs/changes.xml
+++ b/xdocs/changes.xml
@@ -91,6 +91,7 @@ Summary
 <h3>Listeners</h3>
 <ul>
   <li><bug>64988</bug>Sort properties and variables in a human expected order for DebugPostProcessor and DebugSampler</li>
+  <li><bug>63061</bug>Sort View Results in Table in a human expected order</li>
 </ul>
 
 <h3>Timers, Assertions, Config, Pre- &amp; Post-Processors</h3>