diff --git a/include/rtl/string.hxx b/include/rtl/string.hxx index 26b4e2515085..49c838fe837f 100644 --- a/include/rtl/string.hxx +++ b/include/rtl/string.hxx @@ -1221,6 +1221,66 @@ public: } #endif + /** + Check whether this string starts with a given character. + + @param ch the character to be compared + + @return true if and only if the given character appears at the start of this string + + @since LibreOffice 26.8 + */ + bool startsWith(char ch) const + { + return !isEmpty() && pData->buffer[0] == ch; + } + + /** + Check whether this string starts with a given character. + + @param str the character to be compared + + @param rest if this function returns true, then assign a + copy of the remainder of this string to *rest. + + @return true if and only if the given character appears at the start of this string + + @since LibreOffice 26.8 + */ + bool startsWith(char ch, OString* rest) const { + assert(rest); + bool b = startsWith(ch); + + if (b) + *rest = copy(1); + + return b; + } + +#if defined LIBO_INTERNAL_ONLY + /** + Check whether this string starts with a given character. + + @param str the character to be compared + + @param rest if this function returns true, then assign a + copy of the remainder of this string to *rest. + + @return true if and only if the given character appears at the start of this string + + @since LibreOffice 26.8 + */ + bool startsWith(char ch, std::string_view* rest) const { + assert(rest); + bool b = startsWith(ch); + + if (b) + *rest = subView(1); + + return b; + } +#endif + #if defined LIBO_INTERNAL_ONLY /** Check whether this string starts with a given string, ignoring the case of @@ -1595,6 +1655,66 @@ public: && matchL(str, strLength, getLength() - strLength); } + /** + Check whether this string ends with a given character. + + @param ch the character to be compared + + @return true if and only if the given character appears at the end of this string + + @since LibreOffice 26.8 + */ + bool endsWith(char ch) const + { + return !isEmpty() && pData->buffer[pData->length - 1] == ch; + } + + /** + Check whether this string ends with a given character. + + @param str the character to be compared + + @param rest if this function returns true, then assign a + copy of the remainder of this string to *rest. + + @return true if and only if the given character appears at the end of this string + + @since LibreOffice 26.8 + */ + bool endsWith(char ch, OString* rest) const { + assert(rest); + bool b = endsWith(ch); + + if (b) + *rest = copy(0, pData->length - 1); + + return b; + } + +#if defined LIBO_INTERNAL_ONLY + /** + Check whether this string ends with a given character. + + @param str the character to be compared + + @param rest if this function returns true, then assign a + copy of the remainder of this string to *rest. + + @return true if and only if the given character appears at the end of this string + + @since LibreOffice 26.8 + */ + bool endsWith(char ch, std::string_view* rest) const { + assert(rest); + bool b = endsWith(ch); + + if (b) + *rest = subView(0, pData->length - 1); + + return b; + } +#endif + friend bool operator == ( const OString& rStr1, const OString& rStr2 ) { return rStr1.equals(rStr2); } friend bool operator != ( const OString& rStr1, const OString& rStr2 ) diff --git a/include/rtl/ustring.hxx b/include/rtl/ustring.hxx index ac703c8c24bd..979274e32912 100644 --- a/include/rtl/ustring.hxx +++ b/include/rtl/ustring.hxx @@ -1618,6 +1618,66 @@ public: } #endif + /** + Check whether this string starts with a given character. + + @param str the character to be compared + + @return true if and only if the given character appears at the start of this string + + @since LibreOffice 26.8 + */ + bool startsWith(sal_Unicode ch) const + { + return !isEmpty() && pData->buffer[0] == ch; + } + + /** + Check whether this string starts with a given character. + + @param str the character to be compared + + @param rest if this function returns true, then assign a + copy of the remainder of this string to *rest. + + @return true if and only if the given character appears at the start of this string + + @since LibreOffice 26.8 + */ + bool startsWith(sal_Unicode ch, OUString* rest) const { + assert(rest); + bool b = startsWith(ch); + + if (b) + *rest = copy(1); + + return b; + } + +#if defined LIBO_INTERNAL_ONLY + /** + Check whether this string starts with a given character. + + @param str the character to be compared + + @param rest if this function returns true, then assign a + copy of the remainder of this string to *rest. + + @return true if and only if the given character appears at the start of this string + + @since LibreOffice 26.8 + */ + bool startsWith(sal_Unicode ch, std::u16string_view* rest) const { + assert(rest); + bool b = startsWith(ch); + + if (b) + *rest = subView(1); + + return b; + } +#endif + /** Check whether this string starts with a given string, ignoring the case of ASCII letters. @@ -1825,6 +1885,66 @@ public: } #endif + /** + Check whether this string ends with a given character. + + @param str the character to be compared + + @return true if and only if the given character appears at the end of this string + + @since LibreOffice 26.8 + */ + bool endsWith(sal_Unicode ch) const + { + return !isEmpty() && pData->buffer[pData->length - 1] == ch; + } + + /** + Check whether this string ends with a given character. + + @param str the character to be compared + + @param rest if this function returns true, then assign a + copy of the remainder of this string to *rest. + + @return true if and only if the given character appears at the end of this string + + @since LibreOffice 26.8 + */ + bool endsWith(sal_Unicode ch, OUString* rest) const { + assert(rest); + bool b = endsWith(ch); + + if (b) + *rest = copy(0, pData->length - 1); + + return b; + } + +#if defined LIBO_INTERNAL_ONLY + /** + Check whether this string ends with a given character. + + @param str the character to be compared + + @param rest if this function returns true, then assign a + copy of the remainder of this string to *rest. + + @return true if and only if the given character appears at the end of this string + + @since LibreOffice 26.8 + */ + bool endsWith(sal_Unicode ch, std::u16string_view* rest) const { + assert(rest); + bool b = endsWith(ch); + + if (b) + *rest = subView(0, pData->length - 1); + + return b; + } +#endif + #if defined LIBO_INTERNAL_ONLY /** @overload diff --git a/sal/qa/rtl/strings/test_ostring.cxx b/sal/qa/rtl/strings/test_ostring.cxx index 1937edf6863b..597cae93e8b7 100644 --- a/sal/qa/rtl/strings/test_ostring.cxx +++ b/sal/qa/rtl/strings/test_ostring.cxx @@ -21,11 +21,15 @@ private: void testStartsWithIgnoreAsciiCase(); void testCompareTo(); void testUtf8StringLiterals(); + void testStartsWithChar(); + void testEndsWithChar(); CPPUNIT_TEST_SUITE(Test); CPPUNIT_TEST(testStartsWithIgnoreAsciiCase); CPPUNIT_TEST(testCompareTo); CPPUNIT_TEST(testUtf8StringLiterals); + CPPUNIT_TEST(testStartsWithChar); + CPPUNIT_TEST(testEndsWithChar); CPPUNIT_TEST_SUITE_END(); }; @@ -116,6 +120,72 @@ void Test::testUtf8StringLiterals() CPPUNIT_ASSERT_EQUAL(97, int(static_cast(sIn[2]))); } +void Test::testStartsWithChar() +{ + CPPUNIT_ASSERT(!""_ostr.startsWith('.')); + CPPUNIT_ASSERT("."_ostr.startsWith('.')); + CPPUNIT_ASSERT(".foo"_ostr.startsWith('.')); + CPPUNIT_ASSERT(!"."_ostr.startsWith('?')); + CPPUNIT_ASSERT(!".foo"_ostr.startsWith('?')); + + { + OString rest = "not_changed"_ostr; + CPPUNIT_ASSERT(!"foo"_ostr.startsWith('p', &rest)); + CPPUNIT_ASSERT_EQUAL("not_changed"_ostr, rest); + } + + { + OString rest = "must_change"_ostr; + CPPUNIT_ASSERT("/removed_slash"_ostr.startsWith('/', &rest)); + CPPUNIT_ASSERT_EQUAL("removed_slash"_ostr, rest); + } + + { + std::string_view rest = "not_changed"; + CPPUNIT_ASSERT(!"foo"_ostr.startsWith('p', &rest)); + CPPUNIT_ASSERT_EQUAL("not_changed"_ostr, OString(rest)); + } + + { + std::string_view rest = "must_change"; + CPPUNIT_ASSERT("/removed_slash"_ostr.startsWith('/', &rest)); + CPPUNIT_ASSERT_EQUAL("removed_slash"_ostr, OString(rest)); + } +} + +void Test::testEndsWithChar() +{ + CPPUNIT_ASSERT(!""_ostr.endsWith('.')); + CPPUNIT_ASSERT("."_ostr.endsWith('.')); + CPPUNIT_ASSERT("foo."_ostr.endsWith('.')); + CPPUNIT_ASSERT(!"."_ostr.endsWith('?')); + CPPUNIT_ASSERT(!"foo."_ostr.endsWith('?')); + + { + OString rest = "not_changed"_ostr; + CPPUNIT_ASSERT(!"foo"_ostr.endsWith('p', &rest)); + CPPUNIT_ASSERT_EQUAL("not_changed"_ostr, rest); + } + + { + OString rest = "must_change"_ostr; + CPPUNIT_ASSERT("removed_slash/"_ostr.endsWith('/', &rest)); + CPPUNIT_ASSERT_EQUAL("removed_slash"_ostr, rest); + } + + { + std::string_view rest = "not_changed"; + CPPUNIT_ASSERT(!"foo"_ostr.endsWith('p', &rest)); + CPPUNIT_ASSERT_EQUAL("not_changed"_ostr, OString(rest)); + } + + { + std::string_view rest = "must_change"; + CPPUNIT_ASSERT("removed_slash/"_ostr.endsWith('/', &rest)); + CPPUNIT_ASSERT_EQUAL("removed_slash"_ostr, OString(rest)); + } +} + CPPUNIT_TEST_SUITE_REGISTRATION(Test); } diff --git a/sal/qa/rtl/strings/test_oustring_endswith.cxx b/sal/qa/rtl/strings/test_oustring_endswith.cxx index 9ec08fa55045..6439bc498e47 100644 --- a/sal/qa/rtl/strings/test_oustring_endswith.cxx +++ b/sal/qa/rtl/strings/test_oustring_endswith.cxx @@ -33,9 +33,11 @@ class EndsWith: public CppUnit::TestFixture { private: void endsWith(); + void endsWithChar(); CPPUNIT_TEST_SUITE(EndsWith); CPPUNIT_TEST(endsWith); + CPPUNIT_TEST(endsWithChar); CPPUNIT_TEST_SUITE_END(); }; @@ -107,4 +109,39 @@ void test::oustring::EndsWith::endsWith() } } +void test::oustring::EndsWith::endsWithChar() +{ + CPPUNIT_ASSERT(!u""_ustr.endsWith('.')); + CPPUNIT_ASSERT(u"."_ustr.endsWith('.')); + CPPUNIT_ASSERT(u"foo."_ustr.endsWith('.')); + CPPUNIT_ASSERT(u"aĥ"_ustr.endsWith(u'ĥ')); + CPPUNIT_ASSERT(!u"."_ustr.endsWith('?')); + CPPUNIT_ASSERT(!u"foo."_ustr.endsWith('?')); + CPPUNIT_ASSERT(!u"aĥ"_ustr.endsWith(u'ĉ')); + + { + OUString rest = u"not_changed"_ustr; + CPPUNIT_ASSERT(!u"foo"_ustr.endsWith('p', &rest)); + CPPUNIT_ASSERT_EQUAL(u"not_changed"_ustr, rest); + } + + { + OUString rest = u"must_change"_ustr; + CPPUNIT_ASSERT(u"removed_slash/"_ustr.endsWith('/', &rest)); + CPPUNIT_ASSERT_EQUAL(u"removed_slash"_ustr, rest); + } + + { + std::u16string_view rest = u"not_changed"; + CPPUNIT_ASSERT(!u"foo"_ustr.endsWith('p', &rest)); + CPPUNIT_ASSERT_EQUAL(u"not_changed"_ustr, OUString(rest)); + } + + { + std::u16string_view rest = u"must_change"; + CPPUNIT_ASSERT(u"removed_slash/"_ustr.endsWith('/', &rest)); + CPPUNIT_ASSERT_EQUAL(u"removed_slash"_ustr, OUString(rest)); + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/qa/rtl/strings/test_oustring_startswith.cxx b/sal/qa/rtl/strings/test_oustring_startswith.cxx index e27310b82461..b1668a8cde81 100644 --- a/sal/qa/rtl/strings/test_oustring_startswith.cxx +++ b/sal/qa/rtl/strings/test_oustring_startswith.cxx @@ -17,9 +17,11 @@ class StartsWith: public CppUnit::TestFixture { private: void startsWith(); + void startsWithChar(); CPPUNIT_TEST_SUITE(StartsWith); CPPUNIT_TEST(startsWith); + CPPUNIT_TEST(startsWithChar); CPPUNIT_TEST_SUITE_END(); }; @@ -34,4 +36,39 @@ void test::oustring::StartsWith::startsWith() CPPUNIT_ASSERT( !u"foobar"_ustr.startsWith( "oo" )); } +void test::oustring::StartsWith::startsWithChar() +{ + CPPUNIT_ASSERT(!u""_ustr.startsWith('.')); + CPPUNIT_ASSERT(u"."_ustr.startsWith('.')); + CPPUNIT_ASSERT(u".foo"_ustr.startsWith('.')); + CPPUNIT_ASSERT(u"ĉu?"_ustr.startsWith(u'ĉ')); + CPPUNIT_ASSERT(!u"."_ustr.startsWith('?')); + CPPUNIT_ASSERT(!u".foo"_ustr.startsWith('?')); + CPPUNIT_ASSERT(!u"ĉu?"_ustr.startsWith(u'ĥ')); + + { + OUString rest = u"not_changed"_ustr; + CPPUNIT_ASSERT(!u"foo"_ustr.startsWith('p', &rest)); + CPPUNIT_ASSERT_EQUAL(u"not_changed"_ustr, rest); + } + + { + OUString rest = u"must_change"_ustr; + CPPUNIT_ASSERT(u"/removed_slash"_ustr.startsWith('/', &rest)); + CPPUNIT_ASSERT_EQUAL(u"removed_slash"_ustr, rest); + } + + { + std::u16string_view rest = u"not_changed"; + CPPUNIT_ASSERT(!u"foo"_ustr.startsWith('p', &rest)); + CPPUNIT_ASSERT_EQUAL(u"not_changed"_ustr, OUString(rest)); + } + + { + std::u16string_view rest = u"must_change"; + CPPUNIT_ASSERT(u"/removed_slash"_ustr.startsWith('/', &rest)); + CPPUNIT_ASSERT_EQUAL(u"removed_slash"_ustr, OUString(rest)); + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */