From 7b1857d0f3fbbaac537c762d134d746ce0120f34 Mon Sep 17 00:00:00 2001 From: Shane Carr Date: Sat, 10 Feb 2018 15:49:02 +0000 Subject: [PATCH] ICU-13574 Trying to get std::move operator to work on AffixMatcherWarehouse. No luck yet. X-SVN-Rev: 40895 --- icu4c/source/i18n/numparse_affixes.cpp | 183 ++++++++++++------ icu4c/source/i18n/numparse_affixes.h | 38 ++-- icu4c/source/i18n/numparse_impl.cpp | 25 ++- icu4c/source/i18n/numparse_impl.h | 8 +- icu4c/source/i18n/numparse_types.h | 11 ++ .../source/test/intltest/numbertest_parse.cpp | 8 +- .../icu/impl/number/parse/AffixMatcher.java | 14 +- 7 files changed, 181 insertions(+), 106 deletions(-) diff --git a/icu4c/source/i18n/numparse_affixes.cpp b/icu4c/source/i18n/numparse_affixes.cpp index a164f0b70e4..b5d447f192f 100644 --- a/icu4c/source/i18n/numparse_affixes.cpp +++ b/icu4c/source/i18n/numparse_affixes.cpp @@ -17,6 +17,42 @@ using namespace icu::number; using namespace icu::number::impl; +namespace { + +/** + * Helper method to return whether the given AffixPatternMatcher equals the given pattern string. + * Either both arguments must be null or the pattern string inside the AffixPatternMatcher must equal + * the given pattern string. + */ +static bool matched(const AffixPatternMatcher* affix, const UnicodeString& patternString) { + return (affix == nullptr && patternString.isBogus()) || + (affix != nullptr && affix->getPattern() == patternString); +} + +/** + * Helper method to return the length of the given AffixPatternMatcher. Returns 0 for null. + */ +static int32_t length(const AffixPatternMatcher* matcher) { + return matcher == nullptr ? 0 : matcher->getPattern().length(); +} + +/** + * Helper method to return whether (1) both lhs and rhs are null/invalid, or (2) if they are both + * valid, whether they are equal according to operator==. Similar to Java Objects.equals() + */ +static bool equals(const AffixPatternMatcher* lhs, const AffixPatternMatcher* rhs) { + if (lhs == nullptr && rhs == nullptr) { + return true; + } + if (lhs == nullptr || rhs == nullptr) { + return false; + } + return *lhs == *rhs; +} + +} + + AffixPatternMatcherBuilder::AffixPatternMatcherBuilder(const UnicodeString& pattern, AffixTokenMatcherWarehouse& warehouse, IgnorablesMatcher* ignorables) @@ -101,6 +137,9 @@ AffixTokenMatcherWarehouse::AffixTokenMatcherWarehouse(const UChar* currencyCode utils::copyCurrencyCode(this->currencyCode, currencyCode); } +AffixTokenMatcherWarehouse::AffixTokenMatcherWarehouse( + AffixTokenMatcherWarehouse&& src) U_NOEXCEPT = default; + AffixTokenMatcherWarehouse::~AffixTokenMatcherWarehouse() { // Delete the variable number of batches of code point matchers for (int32_t i = 0; i < codePointNumBatches; i++) { @@ -204,16 +243,53 @@ bool AffixPatternMatcher::operator==(const AffixPatternMatcher& other) const { } -AffixMatcherWarehouse::AffixMatcherWarehouse(const AffixPatternProvider& patternInfo, - NumberParserImpl& output, - AffixTokenMatcherWarehouse& warehouse, - const IgnorablesMatcher& ignorables, parse_flags_t parseFlags, - UErrorCode& status) - : fAffixTokenMatcherWarehouse(std::move(warehouse)) { +AffixMatcherWarehouse::AffixMatcherWarehouse(AffixTokenMatcherWarehouse& warehouse) + : fAffixTokenMatcherWarehouse(std::move(warehouse)) {} + +AffixMatcherWarehouse& AffixMatcherWarehouse::operator=(AffixMatcherWarehouse&& src) = default; + +bool AffixMatcherWarehouse::isInteresting(const AffixPatternProvider& patternInfo, + const IgnorablesMatcher& ignorables, parse_flags_t parseFlags, + UErrorCode& status) { + UnicodeStringCharSequence posPrefixString(patternInfo.getString(AffixPatternProvider::AFFIX_POS_PREFIX)); + UnicodeStringCharSequence posSuffixString(patternInfo.getString(AffixPatternProvider::AFFIX_POS_SUFFIX)); + UnicodeStringCharSequence negPrefixString(UnicodeString(u"")); + UnicodeStringCharSequence negSuffixString(UnicodeString(u"")); + if (patternInfo.hasNegativeSubpattern()) { + negPrefixString = UnicodeStringCharSequence(patternInfo.getString(AffixPatternProvider::AFFIX_NEG_PREFIX)); + negSuffixString = UnicodeStringCharSequence(patternInfo.getString(AffixPatternProvider::AFFIX_NEG_SUFFIX)); + } + + if (0 == (parseFlags & PARSE_FLAG_USE_FULL_AFFIXES) && + AffixUtils::containsOnlySymbolsAndIgnorables(posPrefixString, *ignorables.getSet(), status) && + AffixUtils::containsOnlySymbolsAndIgnorables(posSuffixString, *ignorables.getSet(), status) && + AffixUtils::containsOnlySymbolsAndIgnorables(negPrefixString, *ignorables.getSet(), status) && + AffixUtils::containsOnlySymbolsAndIgnorables(negSuffixString, *ignorables.getSet(), status) + // HACK: Plus and minus sign are a special case: we accept them trailing only if they are + // trailing in the pattern string. + && !AffixUtils::containsType(posSuffixString, TYPE_PLUS_SIGN, status) && + !AffixUtils::containsType(posSuffixString, TYPE_MINUS_SIGN, status) && + !AffixUtils::containsType(negSuffixString, TYPE_PLUS_SIGN, status) && + !AffixUtils::containsType(negSuffixString, TYPE_MINUS_SIGN, status)) { + // The affixes contain only symbols and ignorables. + // No need to generate affix matchers. + return false; + } + return true; +} + +AffixMatcherWarehouse AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patternInfo, + MutableMatcherCollection& output, + AffixTokenMatcherWarehouse tokenWarehouse, + const IgnorablesMatcher& ignorables, + parse_flags_t parseFlags, + UErrorCode& status) { if (!isInteresting(patternInfo, ignorables, parseFlags, status)) { - return; + return {}; } + AffixMatcherWarehouse warehouse(tokenWarehouse); + // The affixes have interesting characters, or we are in strict mode. // Use initial capacity of 6, the highest possible number of AffixMatchers. UnicodeString sb; @@ -233,19 +309,21 @@ AffixMatcherWarehouse::AffixMatcherWarehouse(const AffixPatternProvider& pattern bool hasPrefix = false; PatternStringUtils::patternInfoToStringBuilder( patternInfo, true, signum, signDisplay, StandardPlural::OTHER, false, sb); - fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( - sb, warehouse, parseFlags, &hasPrefix, status); - AffixPatternMatcher* prefix = hasPrefix ? &fAffixPatternMatchers[numAffixPatternMatchers++] - : nullptr; + warehouse.fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( + sb, tokenWarehouse, parseFlags, &hasPrefix, status); + AffixPatternMatcher* prefix = hasPrefix + ? &warehouse.fAffixPatternMatchers[numAffixPatternMatchers++] + : nullptr; // Generate Suffix bool hasSuffix = false; PatternStringUtils::patternInfoToStringBuilder( patternInfo, false, signum, signDisplay, StandardPlural::OTHER, false, sb); - fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( - sb, warehouse, parseFlags, &hasSuffix, status); - AffixPatternMatcher* suffix = hasSuffix ? &fAffixPatternMatchers[numAffixPatternMatchers++] - : nullptr; + warehouse.fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( + sb, tokenWarehouse, parseFlags, &hasSuffix, status); + AffixPatternMatcher* suffix = hasSuffix + ? &warehouse.fAffixPatternMatchers[numAffixPatternMatchers++] + : nullptr; if (signum == 1) { posPrefix = prefix; @@ -260,62 +338,37 @@ AffixMatcherWarehouse::AffixMatcherWarehouse(const AffixPatternProvider& pattern // Note: it is indeed possible for posPrefix and posSuffix to both be null. // We still need to add that matcher for strict mode to work. - fAffixMatchers[numAffixMatchers++] = {prefix, suffix, flags}; + warehouse.fAffixMatchers[numAffixMatchers++] = {prefix, suffix, flags}; if (includeUnpaired && prefix != nullptr && suffix != nullptr) { // The following if statements are designed to prevent adding two identical matchers. if (signum == 1 || equals(prefix, posPrefix)) { - fAffixMatchers[numAffixMatchers++] = {prefix, nullptr, flags}; + warehouse.fAffixMatchers[numAffixMatchers++] = {prefix, nullptr, flags}; } if (signum == 1 || equals(suffix, posSuffix)) { - fAffixMatchers[numAffixMatchers++] = {nullptr, suffix, flags}; + warehouse.fAffixMatchers[numAffixMatchers++] = {nullptr, suffix, flags}; } } } // Put the AffixMatchers in order, and then add them to the output. - // TODO -// Collections.sort(matchers, COMPARATOR); -// output.addMatchers(matchers); -} - -bool AffixMatcherWarehouse::isInteresting(const AffixPatternProvider& patternInfo, - const IgnorablesMatcher& ignorables, parse_flags_t parseFlags, - UErrorCode& status) { - UnicodeStringCharSequence posPrefixString(patternInfo.getString(AffixPatternProvider::AFFIX_POS_PREFIX)); - UnicodeStringCharSequence posSuffixString(patternInfo.getString(AffixPatternProvider::AFFIX_POS_SUFFIX)); - UnicodeStringCharSequence negPrefixString(UnicodeString(u"")); - UnicodeStringCharSequence negSuffixString(UnicodeString(u"")); - if (patternInfo.hasNegativeSubpattern()) { - negPrefixString = UnicodeStringCharSequence(patternInfo.getString(AffixPatternProvider::AFFIX_NEG_PREFIX)); - negSuffixString = UnicodeStringCharSequence(patternInfo.getString(AffixPatternProvider::AFFIX_NEG_SUFFIX)); - } - - if (0 == (parseFlags & PARSE_FLAG_USE_FULL_AFFIXES) && - AffixUtils::containsOnlySymbolsAndIgnorables(posPrefixString, *ignorables.getSet(), status) && - AffixUtils::containsOnlySymbolsAndIgnorables(posSuffixString, *ignorables.getSet(), status) && - AffixUtils::containsOnlySymbolsAndIgnorables(negPrefixString, *ignorables.getSet(), status) && - AffixUtils::containsOnlySymbolsAndIgnorables(negSuffixString, *ignorables.getSet(), status) - // HACK: Plus and minus sign are a special case: we accept them trailing only if they are - // trailing in the pattern string. - && !AffixUtils::containsType(posSuffixString, TYPE_PLUS_SIGN, status) && - !AffixUtils::containsType(posSuffixString, TYPE_MINUS_SIGN, status) && - !AffixUtils::containsType(negSuffixString, TYPE_PLUS_SIGN, status) && - !AffixUtils::containsType(negSuffixString, TYPE_MINUS_SIGN, status)) { - // The affixes contain only symbols and ignorables. - // No need to generate affix matchers. - return false; + // Since there are at most 9 elements, do a simple-to-implement bubble sort. + bool madeChanges; + do { + madeChanges = false; + for (int32_t i = 1; i < numAffixMatchers; i++) { + if (warehouse.fAffixMatchers[i - 1].compareTo(warehouse.fAffixMatchers[i]) > 0) { + madeChanges = true; + AffixMatcher temp = std::move(warehouse.fAffixMatchers[i - 1]); + warehouse.fAffixMatchers[i - 1] = std::move(warehouse.fAffixMatchers[i]); + warehouse.fAffixMatchers[i] = std::move(temp); + } + } + } while (madeChanges); + for (int32_t i = 0; i < numAffixMatchers; i++) { + output.addMatcher(warehouse.fAffixMatchers[i]); } - return true; -} -bool AffixMatcherWarehouse::equals(const AffixPatternMatcher* lhs, const AffixPatternMatcher* rhs) { - if (lhs == nullptr && rhs == nullptr) { - return true; - } - if (lhs == nullptr || rhs == nullptr) { - return false; - } - return *lhs == *rhs; + return warehouse; } @@ -390,9 +443,15 @@ void AffixMatcher::postProcess(ParsedNumber& result) const { } } -bool AffixMatcher::matched(const AffixPatternMatcher* affix, const UnicodeString& patternString) { - return (affix == nullptr && patternString.isBogus()) || - (affix != nullptr && affix->getPattern() == patternString); +int8_t AffixMatcher::compareTo(const AffixMatcher& rhs) const { + const AffixMatcher& lhs = *this; + if (length(lhs.fPrefix) != length(rhs.fPrefix)) { + return length(lhs.fPrefix) > length(rhs.fPrefix) ? -1 : 1; + } else if (length(lhs.fSuffix) != length(rhs.fSuffix)) { + return length(lhs.fSuffix) > length(rhs.fSuffix) ? -1 : 1; + } else { + return 0; + } } diff --git a/icu4c/source/i18n/numparse_affixes.h b/icu4c/source/i18n/numparse_affixes.h index 69c68227de5..17175ce7d90 100644 --- a/icu4c/source/i18n/numparse_affixes.h +++ b/icu4c/source/i18n/numparse_affixes.h @@ -60,7 +60,7 @@ class AffixTokenMatcherWarehouse { const UnicodeString* currency2, const DecimalFormatSymbols* dfs, IgnorablesMatcher* ignorables, const Locale* locale); - AffixTokenMatcherWarehouse(AffixTokenMatcherWarehouse&& src) = default; + AffixTokenMatcherWarehouse(AffixTokenMatcherWarehouse&& src) U_NOEXCEPT; ~AffixTokenMatcherWarehouse(); @@ -102,7 +102,7 @@ class AffixTokenMatcherWarehouse { }; -class AffixPatternMatcherBuilder : public TokenConsumer { +class AffixPatternMatcherBuilder : public TokenConsumer, public MutableMatcherCollection { public: AffixPatternMatcherBuilder(const UnicodeString& pattern, AffixTokenMatcherWarehouse& warehouse, IgnorablesMatcher* ignorables); @@ -121,7 +121,7 @@ class AffixPatternMatcherBuilder : public TokenConsumer { AffixTokenMatcherWarehouse& fWarehouse; IgnorablesMatcher* fIgnorables; - void addMatcher(NumberParseMatcher& matcher); + void addMatcher(NumberParseMatcher& matcher) override; }; @@ -153,25 +153,18 @@ class AffixMatcher : public NumberParseMatcher, public UMemory { AffixMatcher(AffixPatternMatcher* prefix, AffixPatternMatcher* suffix, result_flags_t flags); - // static void createMatchers() is the constructor for AffixMatcherWarehouse in C++ - bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; void postProcess(ParsedNumber& result) const override; const UnicodeSet& getLeadCodePoints() override; + int8_t compareTo(const AffixMatcher& rhs) const; + private: AffixPatternMatcher* fPrefix; AffixPatternMatcher* fSuffix; result_flags_t fFlags; - - /** - * Helper method to return whether the given AffixPatternMatcher equals the given pattern string. - * Either both arguments must be null or the pattern string inside the AffixPatternMatcher must equal - * the given pattern string. - */ - static bool matched(const AffixPatternMatcher* affix, const UnicodeString& patternString); }; @@ -182,10 +175,15 @@ class AffixMatcherWarehouse { public: AffixMatcherWarehouse() = default; // WARNING: Leaves the object in an unusable state - // in Java, this is AffixMatcher#createMatchers() - AffixMatcherWarehouse(const AffixPatternProvider& patternInfo, NumberParserImpl& output, - AffixTokenMatcherWarehouse& warehouse, const IgnorablesMatcher& ignorables, - parse_flags_t parseFlags, UErrorCode& status); + AffixMatcherWarehouse(AffixTokenMatcherWarehouse& warehouse); + + AffixMatcherWarehouse& operator=(AffixMatcherWarehouse&& src); + + static AffixMatcherWarehouse createAffixMatchers(const AffixPatternProvider& patternInfo, + MutableMatcherCollection& output, + AffixTokenMatcherWarehouse tokenWarehouse, + const IgnorablesMatcher& ignorables, + parse_flags_t parseFlags, UErrorCode& status); private: // 9 is the limit: positive, zero, and negative, each with prefix, suffix, and prefix+suffix @@ -195,14 +193,10 @@ class AffixMatcherWarehouse { // Store all the tokens used by the AffixPatternMatchers AffixTokenMatcherWarehouse fAffixTokenMatcherWarehouse; + friend class AffixMatcher; + static bool isInteresting(const AffixPatternProvider& patternInfo, const IgnorablesMatcher& ignorables, parse_flags_t parseFlags, UErrorCode& status); - - /** - * Helper method to return whether (1) both lhs and rhs are null/invalid, or (2) if they are both - * valid, whether they are equal according to operator==. Similar to Java Objects.equals() - */ - static bool equals(const AffixPatternMatcher* lhs, const AffixPatternMatcher* rhs); }; diff --git a/icu4c/source/i18n/numparse_impl.cpp b/icu4c/source/i18n/numparse_impl.cpp index efa9b3cab2f..5bf373de58a 100644 --- a/icu4c/source/i18n/numparse_impl.cpp +++ b/icu4c/source/i18n/numparse_impl.cpp @@ -32,18 +32,27 @@ NumberParserImpl::createSimpleParser(const Locale& locale, const UnicodeString& auto* parser = new NumberParserImpl(parseFlags, true); DecimalFormatSymbols symbols(locale, status); - parser->fLocalMatchers.ignorables = std::move(IgnorablesMatcher(unisets::DEFAULT_IGNORABLES)); + parser->fLocalMatchers.ignorables = {unisets::DEFAULT_IGNORABLES}; + IgnorablesMatcher& ignorables = parser->fLocalMatchers.ignorables; -// MatcherFactory factory = new MatcherFactory(); -// factory.currency = Currency.getInstance("USD"); -// factory.symbols = symbols; -// factory.ignorables = ignorables; -// factory.locale = locale; -// factory.parseFlags = parseFlags; + UnicodeString currency1(u"IU$"); + UnicodeString currency2(u"ICU"); ParsedPatternInfo patternInfo; PatternParser::parseToPatternInfo(patternString, patternInfo, status); -// AffixMatcher.createMatchers(patternInfo, parser, factory, ignorables, parseFlags); + + // The following statement sets up the affix matchers. +// AffixMatcherWarehouse warehouse = ; + + parser->fLocalMatchers.affixMatcherWarehouse = std::move(AffixMatcherWarehouse::createAffixMatchers( + patternInfo, + *parser, + AffixTokenMatcherWarehouse( + u"USD", ¤cy1, ¤cy2, &symbols, &ignorables, &locale), + ignorables, + parseFlags, + status)); + Grouper grouper = Grouper::forStrategy(UNUM_GROUPING_AUTO); grouper.setLocaleData(patternInfo, locale); diff --git a/icu4c/source/i18n/numparse_impl.h b/icu4c/source/i18n/numparse_impl.h index abc826f590b..cfae156d564 100644 --- a/icu4c/source/i18n/numparse_impl.h +++ b/icu4c/source/i18n/numparse_impl.h @@ -13,18 +13,19 @@ #include "numparse_scientific.h" #include "unicode/uniset.h" #include "numparse_currency.h" +#include "numparse_affixes.h" U_NAMESPACE_BEGIN namespace numparse { namespace impl { -class NumberParserImpl { +class NumberParserImpl : public MutableMatcherCollection { public: - ~NumberParserImpl(); + virtual ~NumberParserImpl(); static NumberParserImpl* createSimpleParser(const Locale& locale, const UnicodeString& patternString, parse_flags_t parseFlags, UErrorCode& status); - void addMatcher(NumberParseMatcher& matcher); + void addMatcher(NumberParseMatcher& matcher) override; void freeze(); @@ -58,6 +59,7 @@ class NumberParserImpl { DecimalMatcher decimal; ScientificMatcher scientific; CurrencyNamesMatcher currencyNames; + AffixMatcherWarehouse affixMatcherWarehouse; } fLocalMatchers; NumberParserImpl(parse_flags_t parseFlags, bool computeLeads); diff --git a/icu4c/source/i18n/numparse_types.h b/icu4c/source/i18n/numparse_types.h index 8a92dc93fbd..d958a97b9d6 100644 --- a/icu4c/source/i18n/numparse_types.h +++ b/icu4c/source/i18n/numparse_types.h @@ -327,6 +327,17 @@ class NumberParseMatcher { }; +/** + * Interface for use in arguments. + */ +class MutableMatcherCollection { + public: + virtual ~MutableMatcherCollection() = default; + + virtual void addMatcher(NumberParseMatcher& matcher) = 0; +}; + + } // namespace impl } // namespace numparse U_NAMESPACE_END diff --git a/icu4c/source/test/intltest/numbertest_parse.cpp b/icu4c/source/test/intltest/numbertest_parse.cpp index 4fc4da370d7..15cfb40a05c 100644 --- a/icu4c/source/test/intltest/numbertest_parse.cpp +++ b/icu4c/source/test/intltest/numbertest_parse.cpp @@ -76,10 +76,10 @@ void NumberParserTest::testBasic() { // {3, u"a 𝟱𝟭𝟰𝟮𝟯 b", u"a0b", 14, 51423.}, // {3, u"-a 𝟱𝟭𝟰𝟮𝟯 b", u"a0b", 15, -51423.}, // {3, u"a -𝟱𝟭𝟰𝟮𝟯 b", u"a0b", 15, -51423.}, -// {3, u"𝟱𝟭𝟰𝟮𝟯", u"[0];(0)", 10, 51423.}, -// {3, u"[𝟱𝟭𝟰𝟮𝟯", u"[0];(0)", 11, 51423.}, -// {3, u"𝟱𝟭𝟰𝟮𝟯]", u"[0];(0)", 11, 51423.}, -// {3, u"[𝟱𝟭𝟰𝟮𝟯]", u"[0];(0)", 12, 51423.}, + {3, u"𝟱𝟭𝟰𝟮𝟯", u"[0];(0)", 10, 51423.}, + {3, u"[𝟱𝟭𝟰𝟮𝟯", u"[0];(0)", 11, 51423.}, + {3, u"𝟱𝟭𝟰𝟮𝟯]", u"[0];(0)", 11, 51423.}, + {3, u"[𝟱𝟭𝟰𝟮𝟯]", u"[0];(0)", 12, 51423.}, // {3, u"(𝟱𝟭𝟰𝟮𝟯", u"[0];(0)", 11, -51423.}, // {3, u"𝟱𝟭𝟰𝟮𝟯)", u"[0];(0)", 11, -51423.}, // {3, u"(𝟱𝟭𝟰𝟮𝟯)", u"[0];(0)", 12, -51423.}, diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java index 6fccdc29321..c749cd409ff 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java @@ -29,15 +29,15 @@ public class AffixMatcher implements NumberParseMatcher { */ public static final Comparator COMPARATOR = new Comparator() { @Override - public int compare(AffixMatcher o1, AffixMatcher o2) { - if (length(o1.prefix) != length(o2.prefix)) { - return length(o1.prefix) > length(o2.prefix) ? -1 : 1; - } else if (length(o1.suffix) != length(o2.suffix)) { - return length(o1.suffix) > length(o2.suffix) ? -1 : 1; - } else if (!o1.equals(o2)) { + public int compare(AffixMatcher lhs, AffixMatcher rhs) { + if (length(lhs.prefix) != length(rhs.prefix)) { + return length(lhs.prefix) > length(rhs.prefix) ? -1 : 1; + } else if (length(lhs.suffix) != length(rhs.suffix)) { + return length(lhs.suffix) > length(rhs.suffix) ? -1 : 1; + } else if (!lhs.equals(rhs)) { // If the prefix and suffix are the same length, arbitrarily break ties. // We can't return zero unless the elements are equal. - return o1.hashCode() > o2.hashCode() ? -1 : 1; + return lhs.hashCode() > rhs.hashCode() ? -1 : 1; } else { return 0; } -- 2.40.0