Repository: MegaMek/mekhq
Pull request #6585: Fix #6563: Fixed In Transit Sorting in Warehouse Tab
Author: login: IllianiBird
Labels: name: GUI
name: UX
description: User experience
Created: 2025-04-07T20:40:28Z | Merged: 2025-04-11T23:18:43Z

- Implemented a new `StringAndThenNumberSorter` comparator to handle sorting of strings with embedded numeric values.
- Updated `WarehouseTab` to use `StringAndThenNumberSorter` for the `COL_STATUS` column in the parts table.
- Enhanced sorting behavior to properly order entries with mixed alphanumeric content, i.e. `In Transit (6 days)` will appear before `In Transit (10 days)`, but both will appear after `Functional`.

Fix #6563

### Dev Notes
RegEx is freakin' sorcery, I swear.

I stole this pattern from the internet so don't ask me _how_ it works, I just know it _does_.

Linked issue #6563: [Issue] Leading 0's needed in delivery times to sort correctly 01,02,03 days vs 1,20,2,35,37,38,3 days
### Brief Description *

Bug for Mekhq Should have a leading 0 on Warehouse "Status" column for delivery times....2 gets put AFTER 20 days.... if you had leading 0's 01 days, 02 days, 03 days, they'd sort correct I think :)

I dont like how complex it is to report something.   I help bug fix for a variety of games, I screen shot, markup, drop into bug channels typically on discord.  Maybe someone would like to offer that and then transfer it here.  I likely wont report any more in the future.

### 3. Steps to Reproduce

_No response_

### Attach Files

![Image](https://github.com/user-attachments/assets/60e84f06-ed9b-4c3a-8a0c-c6eecbd37dfa)



### Severity *

Critical (Game-breaking/Crash): The game crashes or a core feature (like saving, loading, or network connection) is completely unusable.

### MegaMek Suite Version *

50.04

### Operating System *

Window11

### Java Version *

?? no idea one of the newest

### Final Verification

- [x] I confirm this is a single, unique issue that hasn't been reported before
- [x] I have included all necessary information and files to help reproduce this issue
- [x] I have asked on MegaMek Discord about this issue
- [x] I have confirmed this issue is being opened on the correct repository: MegaMek, MegaMekLab, or MekHQ
Linked issue comments: user: login: IllianiBird
created_at: 2025-04-06T05:50:29Z
body: While I appreciate that it's a bit of a chore, please remember that we're _not_ a professional game studio. Nor do we have the backing of some publisher. We're not even an indie studio. We literally have a budget of 0 dollars. We're a group of passionate fans donating our time and expertise to craft what is probably the best Mek management simulator in the world.

Why does that concern you? Well, it means we don't have a group of staff whose job it is to track down bugs on Discord, log them, and then make sure they're flagged with the appropriate teams. Instead, we need to lean on users, like yourself, to help carry the load.

After all, what would you prefer we spend our time doing: scrolling through Discord and checking for bugs, or actually fixing those bugs?
author_association: COLLABORATOR
user: login: rjhancock
created_at: 2025-04-06T05:56:38Z
body: 1) Leading zeros wont solve the problem and it can be solved by simply converting the time in days to an Integer and sorting that way.
2) We ask you check on Discord FIRST as we've had a history of people report duplicate bugs or the issue may already be fixed either in a later version OR in a nightly.
3) If neither of those are the case, we ask they report the bug here so that it gets logged properly.

This is the process that works for us as we are all volunteers. We do this because we CHOOSE to. We already have a rather large back log of issues that we are working on PLUS features we want to implement PLUS our own lives to attend to.

This bug is a minor inconvenience at best.
author_association: COLLABORATOR
user: login: kevinschulthies
created_at: 2025-04-06T17:42:37Z
body: You guys do epic, work thanks 😊 Yeah I jump into a couple discussions on discord. Been enjoying everyone’s work for years with Megamek and thought I’d make my first quick suggestion (little did I release how complex your feedback system was).  

 

I didn’t mean any disrespect but was trying to help, but I’ll just stay silent to everything because of the complexity of it all and will just enjoy the system for what it is.  The current way to report anything is crazily complex (and im sure it helps in some ways isolate or categorizie bugs etc) 

 

Thanks again, 

Kevin

 

 

From: Richard J Hancock ***@***.***> 
Sent: April 5, 2025 11:57 PM
To: MegaMek/mekhq ***@***.***>
Cc: kevinschulthies ***@***.***>; Author ***@***.***>
Subject: Re: [MegaMek/mekhq] [Issue] Leading 0's needed in delivery times to sort correctly 01,02,03 days vs 1,20,2,35,37,38,3 days (Issue #6563)

 

1.	Leading zeros wont solve the problem and it can be solved by simply converting the time in days to an Integer and sorting that way.
2.	We ask you check on Discord FIRST as we've had a history of people report duplicate bugs or the issue may already be fixed either in a later version OR in a nightly.
3.	If neither of those are the case, we ask they report the bug here so that it gets logged properly.

This is the process that works for us as we are all volunteers. We do this because we CHOOSE to. We already have a rather large back log of issues that we are working on PLUS features we want to implement PLUS our own lives to attend to.

This bug is a minor inconvenience at best.

—
Reply to this email directly, view it on GitHub <https://github.com/MegaMek/mekhq/issues/6563#issuecomment-2781236757> , or unsubscribe <https://github.com/notifications/unsubscribe-auth/BKMGIFZW6KSOHVSVNGPF4CD2YC62ZAVCNFSM6AAAAAB2RJ5GCCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDOOBRGIZTMNZVG4> .
You are receiving this because you authored the thread.  <https://github.com/notifications/beacon/BKMGIF55LAPPM4UY27TQTQD2YC62ZA5CNFSM6AAAAAB2RJ5GCCWGG33NNVSW45C7OR4XAZNMJFZXG5LFINXW23LFNZ2KUY3PNVWWK3TUL5UWJTVFYZHBK.gif> Message ID: < ***@***.***> ***@***.***>

  <https://avatars.githubusercontent.com/u/460240?s=20&v=4> rjhancock left a comment (MegaMek/mekhq#6563) <https://github.com/MegaMek/mekhq/issues/6563#issuecomment-2781236757> 

1.	Leading zeros wont solve the problem and it can be solved by simply converting the time in days to an Integer and sorting that way.
2.	We ask you check on Discord FIRST as we've had a history of people report duplicate bugs or the issue may already be fixed either in a later version OR in a nightly.
3.	If neither of those are the case, we ask they report the bug here so that it gets logged properly.

This is the process that works for us as we are all volunteers. We do this because we CHOOSE to. We already have a rather large back log of issues that we are working on PLUS features we want to implement PLUS our own lives to attend to.

This bug is a minor inconvenience at best.

—
Reply to this email directly, view it on GitHub <https://github.com/MegaMek/mekhq/issues/6563#issuecomment-2781236757> , or unsubscribe <https://github.com/notifications/unsubscribe-auth/BKMGIFZW6KSOHVSVNGPF4CD2YC62ZAVCNFSM6AAAAAB2RJ5GCCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDOOBRGIZTMNZVG4> .
You are receiving this because you authored the thread.  <https://github.com/notifications/beacon/BKMGIF55LAPPM4UY27TQTQD2YC62ZA5CNFSM6AAAAAB2RJ5GCCWGG33NNVSW45C7OR4XAZNMJFZXG5LFINXW23LFNZ2KUY3PNVWWK3TUL5UWJTVFYZHBK.gif> Message ID: < ***@***.***> ***@***.***>


author_association: NONE
user: login: rjhancock
created_at: 2025-04-06T18:00:48Z
body: The current system is a result of the user base not providing us with enough context to work with issues that arise, submitting duplicate tickets without searching for them first, or reporting bugs that have either been fixed in a later released version or the nightly and soon to be released.

We ask the users to do a little extra work (and yes, it is a LITTLE extra work) to ensure we the team get what we need and can focus on fixing bugs and adding features instead of continently asking questions to get anything resembling what we need to determine if the issue is a bug, user error, and a feature request.

We do not have the resources to keep doing that for every issue. You can see the backlog in the three projects has exceeded several THOUSAND issues that need to be worked through still and triaged at a minimal. We need help. Period. Part of that help is having you, the user, do some of the leg work before submitting a ticket to make sure we don't spend hours trying to triage it. Hours we could have spent fixing the issue or any of a hundred others.

So the process is NOT needlessly complex, it is as complex as it is to ensure we get actionable tickets to make it better for all.

Again, we are a volunteer team with limited time to fix bugs and add features. The more work the submitter does to get us the details we need, the faster we can get it fixed and add more features.
author_association: COLLABORATOR

Diff:
diff --git a/MekHQ/src/mekhq/gui/WarehouseTab.java b/MekHQ/src/mekhq/gui/WarehouseTab.java
index db1682a41a6..c44821f51be 100644
--- a/MekHQ/src/mekhq/gui/WarehouseTab.java
+++ b/MekHQ/src/mekhq/gui/WarehouseTab.java
@@ -66,6 +66,7 @@
 import mekhq.gui.sorter.FormattedNumberSorter;
 import mekhq.gui.sorter.PartsDetailSorter;
 import mekhq.gui.sorter.TechSorter;
+import mekhq.gui.sorter.WarehouseStatusSorter;
 import mekhq.gui.utilities.JScrollPaneWithSpeed;
 
 /**
@@ -197,6 +198,7 @@ public void initTab() {
         partsSorter = new TableRowSorter<>(partsModel);
         partsSorter.setComparator(PartsTableModel.COL_COST, new FormattedNumberSorter());
         partsSorter.setComparator(PartsTableModel.COL_DETAIL, new PartsDetailSorter());
+        partsSorter.setComparator(PartsTableModel.COL_STATUS, new WarehouseStatusSorter());
         partsSorter.setComparator(PartsTableModel.COL_TOTAL_COST, new FormattedNumberSorter());
         partsTable.setRowSorter(partsSorter);
         TableColumn column;
diff --git a/MekHQ/src/mekhq/gui/sorter/WarehouseStatusSorter.java b/MekHQ/src/mekhq/gui/sorter/WarehouseStatusSorter.java
new file mode 100644
index 00000000000..7eec4a38c23
--- /dev/null
+++ b/MekHQ/src/mekhq/gui/sorter/WarehouseStatusSorter.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPL),
+ * version 3 or (at your option) any later version,
+ * as published by the Free Software Foundation.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * A copy of the GPL should have been included with this project;
+ * if not, see <https://www.gnu.org/licenses/>.
+ *
+ * NOTICE: The MegaMek organization is a non-profit group of volunteers
+ * creating free software for the BattleTech community.
+ *
+ * MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
+ * of The Topps Company, Inc. All Rights Reserved.
+ *
+ * Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
+ * InMediaRes Productions, LLC.
+ */
+package mekhq.gui.sorter;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import megamek.codeUtilities.MathUtility;
+import megamek.logging.MMLogger;
+
+/**
+ * Custom comparator to sort strings alphabetically, unless the string contains a phrase indicating a number of "day
+ * (s)" (e.g., "In Transit (5 days)"). Strings with "day(s)" are sorted numerically by the number of days first, while
+ * those without are sorted alphabetically.
+ *
+ * <p>Example List Before Sorting:</p>
+ * <pre>["Functional", "Broken", "Damaged", "In Transit (5 days)", "In Transit (1 day)", "Transit (101 days)"]</pre>
+ *
+ * <p>Example List After Sorting:</p>
+ * <pre>["Broken", "Damaged", "Functional", "In Transit (1 day)", "In Transit (5 days)", "Transit (101 days)"]</pre>
+ *
+ * @author Illiani
+ * @since 0.50.05
+ */
+public class WarehouseStatusSorter implements Comparator<String> {
+    private static final MMLogger logger = MMLogger.create(WarehouseStatusSorter.class);
+
+    /**
+     * Regular expression pattern to locate the number of "days" in a string.
+     *
+     * <p>Matches strings like "(5 days)" or "(1 day)" and captures the numeric part.</p>
+     */
+    private static final Pattern DAYS_PATTERN = Pattern.compile("\\((\\d+)\\s*day(s)?\\)");
+
+    /**
+     * Compares two strings and determines their sorting order.
+     *
+     * <ul>
+     *     <li>If both strings contain "day(s)", they are compared numerically based on the number of days.</li>
+     *     <li>If only one string contains "day(s)", it is considered greater (sorted after) strings without "day(s)".</li>
+     *     <li>If neither string contains "day(s)", they are compared alphabetically (case-insensitive).</li>
+     * </ul>
+     *
+     * @param firstString  the first string to be compared
+     * @param secondString the second string to be compared
+     *
+     * @return a negative integer, zero, or a positive integer as the first string is less than, equal to, or greater
+     *       than the second string respectively
+     *
+     * @author Illiani
+     * @since 0.50.05
+     */
+    @Override
+    public int compare(String firstString, String secondString) {
+        // Check if either string contains "day" or "days"
+        boolean firstStringContainsDays = DAYS_PATTERN.matcher(firstString).find();
+        boolean secondStringContainsDays = DAYS_PATTERN.matcher(secondString).find();
+
+        // If both strings contain "day(s)", compare numerically
+        if (firstStringContainsDays && secondStringContainsDays) {
+            int firstStringDayCount = extractDays(firstString);
+            int secondStringDayCount = extractDays(secondString);
+            return Integer.compare(firstStringDayCount, secondStringDayCount);
+        }
+        // If only the first string contains "day(s)", it is greater
+        else if (firstStringContainsDays) {
+            return 1;
+        }
+        // If only the second string contains "day(s)", it is greater
+        else if (secondStringContainsDays) {
+            return -1;
+        }
+        // If neither contains "day(s)", fallback to alphabetical comparison
+        else {
+            return firstString.compareToIgnoreCase(secondString);
+        }
+    }
+
+    /**
+     * Extracts the numeric value representing "days" from a string.
+     *
+     * <p>The method searches for a pattern matching a number followed by "day(s)" (e.g., "(5 days)"). If found, it
+     * parses and returns the numeric value.</p>
+     *
+     * <p>If no numeric value is found, or parsing fails, it defaults to {@link Integer#MAX_VALUE}.</p>
+     *
+     * @param string the input string to search
+     *
+     * @return the numeric representation of "days", or {@link Integer#MAX_VALUE} if no match is found
+     *
+     * @author Illiani
+     * @since 0.50.05
+     */
+    private int extractDays(String string) {
+        Matcher matcher = DAYS_PATTERN.matcher(string);
+        if (matcher.find()) {
+            // We're using the group value here, as we want to compare the entire number not just the first integer
+            // we hit.
+            return MathUtility.parseInt(matcher.group(1), Integer.MAX_VALUE);
+        }
+
+        // This is a fallback value, in the event the Regex picks up that the String contains numbers, but for some
+        // reason can't parse them. I don't expect this to ever be used, but if it is, we'll want to address the error.
+        logger.error("Matcher failed to extract Integer from String: {}", string);
+
+        return Integer.MAX_VALUE;
+    }
+}


Reviews:
user: login: rjhancock
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T21:29:36Z
user: login: rjhancock
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T21:31:14Z
user: login: IllianiBird
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T23:46:53Z
user: login: IllianiBird
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T23:47:22Z
user: login: psikomonkie
body: Nice! 

I think I prefer c872c04fe14d88d0479de8172bd9896b70cb731e. While both currently work, I think the older commit version would be more robust. The older commit implementation will natively support translations as long as it follows the pattern of <Meaningful letters to sort by><meaningful number to sort by>. The older commit implementation will better support if a second warehouse part status gets "days" added to it, like days remaining for repairing something broken. I know this contradicts what we discussed yesterday but after comparing the two and taking translation support more seriously I think the original commit will be more robust.

Does anyone else have any thoughts on this?
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T20:43:49Z
user: login: rjhancock
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-11T18:41:47Z

Review comments:
diff_hunk: @@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPL),
+ * version 3 or (at your option) any later version,
+ * as published by the Free Software Foundation.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * A copy of the GPL should have been included with this project;
+ * if not, see <https://www.gnu.org/licenses/>.
+ *
+ * NOTICE: The MegaMek organization is a non-profit group of volunteers
+ * creating free software for the BattleTech community.
+ *
+ * MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
+ * of The Topps Company, Inc. All Rights Reserved.
+ *
+ * Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
+ * InMediaRes Productions, LLC.
+ */
+package mekhq.gui.sorter;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A comparator for sorting strings based on their natural lexicographic order, while properly handling embedded numeric
+ * values in the strings.
+ *
+ * <p>The comparator compares strings in chunks of numeric and non-numeric parts:</p>
+ * <ul>
+ *     <li>Non-numeric parts are compared lexicographically (case-insensitive).</li>
+ *     <li>Numeric parts are compared numerically, ensuring that numbers are sorted based on their numerical value
+ *     rather than their string representation (e.g., "2" is less than "106").</li>
+ * </ul>
+ *
+ * <p>This sorter is useful for cases where strings have mixed content (letters and numbers), and the numeric ordering
+ * needs to take precedence where applicable.</p>
+ *
+ * <p>For example, the following list:</p>
+ * <pre>["Destroyed", "Functional", "In Transit (2 days)", "In Transit (106 days)", "Transit (7 days)"]</pre>
+ *
+ * <p>Will be sorted as:</p>
+ * <pre>["Destroyed", "Functional", "In Transit (2 days)", ""Transit (7 days)", In Transit (106 days)"]</pre>
+ *
+ * @author Illiani
+ * @since 0.50.05
+ */
+public class StringAndThenNumberSorter implements Comparator<String> {
+    /**
+     * A compiled RegEx pattern used to match and separate numeric and non-numeric parts of a string in the order they
+     * appear.
+     *
+     * <p>The pattern alternates between sequences of digits (numeric parts) and non-digit characters (non-numeric
+     * parts). This pattern is useful for parsing strings that need to be interpreted in a manner where numeric and
+     * non-numeric parts are processed differently.</p>
+     */
+    private static final Pattern NUMBER_PATTERN = Pattern.compile("(\\d+|\\D+)");
+
+    /**
path: MekHQ/src/mekhq/gui/sorter/StringAndThenNumberSorter.java
user: login: rjhancock
body: Pattern is incorrect. that will match all numbers and non-numbers.

To match all numbers it is `\d+` (or for that variation `\\d+`)
created_at: 2025-04-07T21:29:10Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPL),
+ * version 3 or (at your option) any later version,
+ * as published by the Free Software Foundation.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * A copy of the GPL should have been included with this project;
+ * if not, see <https://www.gnu.org/licenses/>.
+ *
+ * NOTICE: The MegaMek organization is a non-profit group of volunteers
+ * creating free software for the BattleTech community.
+ *
+ * MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
+ * of The Topps Company, Inc. All Rights Reserved.
+ *
+ * Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
+ * InMediaRes Productions, LLC.
+ */
+package mekhq.gui.sorter;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A comparator for sorting strings based on their natural lexicographic order, while properly handling embedded numeric
+ * values in the strings.
+ *
+ * <p>The comparator compares strings in chunks of numeric and non-numeric parts:</p>
+ * <ul>
+ *     <li>Non-numeric parts are compared lexicographically (case-insensitive).</li>
+ *     <li>Numeric parts are compared numerically, ensuring that numbers are sorted based on their numerical value
+ *     rather than their string representation (e.g., "2" is less than "106").</li>
+ * </ul>
+ *
+ * <p>This sorter is useful for cases where strings have mixed content (letters and numbers), and the numeric ordering
+ * needs to take precedence where applicable.</p>
+ *
+ * <p>For example, the following list:</p>
+ * <pre>["Destroyed", "Functional", "In Transit (2 days)", "In Transit (106 days)", "Transit (7 days)"]</pre>
+ *
+ * <p>Will be sorted as:</p>
+ * <pre>["Destroyed", "Functional", "In Transit (2 days)", ""Transit (7 days)", In Transit (106 days)"]</pre>
+ *
+ * @author Illiani
+ * @since 0.50.05
+ */
+public class StringAndThenNumberSorter implements Comparator<String> {
+    /**
+     * A compiled RegEx pattern used to match and separate numeric and non-numeric parts of a string in the order they
+     * appear.
+     *
+     * <p>The pattern alternates between sequences of digits (numeric parts) and non-digit characters (non-numeric
+     * parts). This pattern is useful for parsing strings that need to be interpreted in a manner where numeric and
+     * non-numeric parts are processed differently.</p>
+     */
+    private static final Pattern NUMBER_PATTERN = Pattern.compile("(\\d+|\\D+)");
+
+    /**
path: MekHQ/src/mekhq/gui/sorter/StringAndThenNumberSorter.java
user: login: rjhancock
body: If it's only displayed in days, the `\\d+` will work. Otherwise you'll need to adjust it to be `\\(((\\d+) (\\D+)\\)` to capture the number of days AND interval to compare days, weeks, months, years...
created_at: 2025-04-07T21:31:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPL),
+ * version 3 or (at your option) any later version,
+ * as published by the Free Software Foundation.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * A copy of the GPL should have been included with this project;
+ * if not, see <https://www.gnu.org/licenses/>.
+ *
+ * NOTICE: The MegaMek organization is a non-profit group of volunteers
+ * creating free software for the BattleTech community.
+ *
+ * MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
+ * of The Topps Company, Inc. All Rights Reserved.
+ *
+ * Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
+ * InMediaRes Productions, LLC.
+ */
+package mekhq.gui.sorter;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A comparator for sorting strings based on their natural lexicographic order, while properly handling embedded numeric
+ * values in the strings.
+ *
+ * <p>The comparator compares strings in chunks of numeric and non-numeric parts:</p>
+ * <ul>
+ *     <li>Non-numeric parts are compared lexicographically (case-insensitive).</li>
+ *     <li>Numeric parts are compared numerically, ensuring that numbers are sorted based on their numerical value
+ *     rather than their string representation (e.g., "2" is less than "106").</li>
+ * </ul>
+ *
+ * <p>This sorter is useful for cases where strings have mixed content (letters and numbers), and the numeric ordering
+ * needs to take precedence where applicable.</p>
+ *
+ * <p>For example, the following list:</p>
+ * <pre>["Destroyed", "Functional", "In Transit (2 days)", "In Transit (106 days)", "Transit (7 days)"]</pre>
+ *
+ * <p>Will be sorted as:</p>
+ * <pre>["Destroyed", "Functional", "In Transit (2 days)", ""Transit (7 days)", In Transit (106 days)"]</pre>
+ *
+ * @author Illiani
+ * @since 0.50.05
+ */
+public class StringAndThenNumberSorter implements Comparator<String> {
+    /**
+     * A compiled RegEx pattern used to match and separate numeric and non-numeric parts of a string in the order they
+     * appear.
+     *
+     * <p>The pattern alternates between sequences of digits (numeric parts) and non-digit characters (non-numeric
+     * parts). This pattern is useful for parsing strings that need to be interpreted in a manner where numeric and
+     * non-numeric parts are processed differently.</p>
+     */
+    private static final Pattern NUMBER_PATTERN = Pattern.compile("(\\d+|\\D+)");
+
+    /**
path: MekHQ/src/mekhq/gui/sorter/StringAndThenNumberSorter.java
user: login: IllianiBird
body: I'm not sure what I need to do here, could you provide a little more information?

For context, the table column uses a mix of both numbers and non-numbers. For example, the column might have 'damaged', 'functional', 'in transit (5 days)', 'in transit (353 days)'. The intention is that it will sort based on letters and then, when it hits a number, start sorting based on that.

Right now, the implementation visually works correctly, but if I move it to `\\d+` I run into `Comparison method violates its general contract!` errors, presumably because I'm left with empty strings to compare because all the non-digits get filtered out.
created_at: 2025-04-07T23:46:52Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPL),
+ * version 3 or (at your option) any later version,
+ * as published by the Free Software Foundation.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * A copy of the GPL should have been included with this project;
+ * if not, see <https://www.gnu.org/licenses/>.
+ *
+ * NOTICE: The MegaMek organization is a non-profit group of volunteers
+ * creating free software for the BattleTech community.
+ *
+ * MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
+ * of The Topps Company, Inc. All Rights Reserved.
+ *
+ * Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
+ * InMediaRes Productions, LLC.
+ */
+package mekhq.gui.sorter;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A comparator for sorting strings based on their natural lexicographic order, while properly handling embedded numeric
+ * values in the strings.
+ *
+ * <p>The comparator compares strings in chunks of numeric and non-numeric parts:</p>
+ * <ul>
+ *     <li>Non-numeric parts are compared lexicographically (case-insensitive).</li>
+ *     <li>Numeric parts are compared numerically, ensuring that numbers are sorted based on their numerical value
+ *     rather than their string representation (e.g., "2" is less than "106").</li>
+ * </ul>
+ *
+ * <p>This sorter is useful for cases where strings have mixed content (letters and numbers), and the numeric ordering
+ * needs to take precedence where applicable.</p>
+ *
+ * <p>For example, the following list:</p>
+ * <pre>["Destroyed", "Functional", "In Transit (2 days)", "In Transit (106 days)", "Transit (7 days)"]</pre>
+ *
+ * <p>Will be sorted as:</p>
+ * <pre>["Destroyed", "Functional", "In Transit (2 days)", ""Transit (7 days)", In Transit (106 days)"]</pre>
+ *
+ * @author Illiani
+ * @since 0.50.05
+ */
+public class StringAndThenNumberSorter implements Comparator<String> {
+    /**
+     * A compiled RegEx pattern used to match and separate numeric and non-numeric parts of a string in the order they
+     * appear.
+     *
+     * <p>The pattern alternates between sequences of digits (numeric parts) and non-digit characters (non-numeric
+     * parts). This pattern is useful for parsing strings that need to be interpreted in a manner where numeric and
+     * non-numeric parts are processed differently.</p>
+     */
+    private static final Pattern NUMBER_PATTERN = Pattern.compile("(\\d+|\\D+)");
+
+    /**
path: MekHQ/src/mekhq/gui/sorter/StringAndThenNumberSorter.java
user: login: IllianiBird
body: Also, if it helps, it only appears in batches of 'days', we don't convert that to months or similar
created_at: 2025-04-07T23:47:22Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPL),
+ * version 3 or (at your option) any later version,
+ * as published by the Free Software Foundation.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * A copy of the GPL should have been included with this project;
+ * if not, see <https://www.gnu.org/licenses/>.
+ *
+ * NOTICE: The MegaMek organization is a non-profit group of volunteers
+ * creating free software for the BattleTech community.
+ *
+ * MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
+ * of The Topps Company, Inc. All Rights Reserved.
+ *
+ * Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
+ * InMediaRes Productions, LLC.
+ */
+package mekhq.gui.sorter;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import megamek.codeUtilities.MathUtility;
+import megamek.logging.MMLogger;
+
+/**
+ * Custom comparator to sort strings alphabetically, unless the string contains a phrase indicating a number of "day
+ * (s)" (e.g., "In Transit (5 days)"). Strings with "day(s)" are sorted numerically by the number of days first, while
+ * those without are sorted alphabetically.
+ *
+ * <p>Example List Before Sorting:</p>
+ * <pre>["Functional", "Broken", "Damaged", "In Transit (5 days)", "In Transit (1 day)", "Transit (101 days)"]</pre>
+ *
+ * <p>Example List After Sorting:</p>
+ * <pre>["Broken", "Damaged", "Functional", "In Transit (1 day)", "In Transit (5 days)", "Transit (101 days)"]</pre>
+ *
+ * @author Illiani
+ * @since 0.50.05
+ */
+public class WarehouseStatusSorter implements Comparator<String> {
+    private static final MMLogger logger = MMLogger.create(WarehouseStatusSorter.class);
+
+    /**
+     * Regular expression pattern to locate the number of "days" in a string.
+     *
+     * <p>Matches strings like "(5 days)" or "(1 day)" and captures the numeric part.</p>
+     */
+    private static final Pattern DAYS_PATTERN = Pattern.compile("\\((\\d+)\\s*day(s)?\\)");
path: MekHQ/src/mekhq/gui/sorter/WarehouseStatusSorter.java
user: login: psikomonkie
body: You can make a group a "non-capturing" group by starting it with ?: `day(?:s)?`. Though in this case you don't need the capture group at all, you should get the same result with `days?`.
created_at: 2025-04-08T20:36:01Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-04-07T20:47:11Z
body: ## [Codecov](https://app.codecov.io/gh/MegaMek/mekhq/pull/6585?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek) Report
Attention: Patch coverage is `0%` with `20 lines` in your changes missing coverage. Please review.
> Project coverage is 11.43%. Comparing base [(`f2af508`)](https://app.codecov.io/gh/MegaMek/mekhq/commit/f2af508fade9f140fc9753e122cd0c3460081af8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek) to head [(`f5829de`)](https://app.codecov.io/gh/MegaMek/mekhq/commit/f5829de636cc788e1acec747b4f16453923a3bd7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek).
> Report is 67 commits behind head on master.

| [Files with missing lines](https://app.codecov.io/gh/MegaMek/mekhq/pull/6585?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek) | Patch % | Lines |
|---|---|---|
| [...HQ/src/mekhq/gui/sorter/WarehouseStatusSorter.java](https://app.codecov.io/gh/MegaMek/mekhq/pull/6585?src=pr&el=tree&filepath=MekHQ%2Fsrc%2Fmekhq%2Fgui%2Fsorter%2FWarehouseStatusSorter.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek#diff-TWVrSFEvc3JjL21la2hxL2d1aS9zb3J0ZXIvV2FyZWhvdXNlU3RhdHVzU29ydGVyLmphdmE=) | 0.00% | [19 Missing :warning: ](https://app.codecov.io/gh/MegaMek/mekhq/pull/6585?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek) |
| [MekHQ/src/mekhq/gui/WarehouseTab.java](https://app.codecov.io/gh/MegaMek/mekhq/pull/6585?src=pr&el=tree&filepath=MekHQ%2Fsrc%2Fmekhq%2Fgui%2FWarehouseTab.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek#diff-TWVrSFEvc3JjL21la2hxL2d1aS9XYXJlaG91c2VUYWIuamF2YQ==) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/MegaMek/mekhq/pull/6585?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##             master    #6585   +/-   ##
=========================================
  Coverage     11.43%   11.43%           
- Complexity     6451     6452    +1     
=========================================
  Files          1087     1088    +1     
  Lines        139451   139468   +17     
  Branches      21560    21563    +3     
=========================================
+ Hits          15943    15951    +8     
- Misses       121916   121923    +7     
- Partials       1592     1594    +2     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/MegaMek/mekhq/pull/6585?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MegaMek).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: IllianiBird
created_at: 2025-04-08T20:21:47Z
body: - Replaced `StringAndThenNumberSorter` with a new `WarehouseStatusSorter`.
- Updated the comparator logic sort strings numerically for "day(s)" and alphabetically otherwise.
- Renamed and refactored the sorter to align with its updated functionality and naming conventions.
- Adjusted the `WarehouseTab` to utilize the new `WarehouseStatusSorter`.
author_association: COLLABORATOR
user: login: IllianiBird
created_at: 2025-04-08T20:46:05Z
body: > Does anyone else have any thoughts on this?


![](https://media.tenor.com/UlIwB2YVcGwAAAAM/waah-waa.gif)



author_association: COLLABORATOR