lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx |  228 
++++------
 lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx |    6 
 2 files changed, 95 insertions(+), 139 deletions(-)

New commits:
commit 3579343ec00adcbf54938e59d27cc52d11ddb920
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Thu Mar 9 13:11:14 2023 +0300
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Thu Mar 9 12:08:16 2023 +0000

    Some refactoring and deduplication
    
    Change-Id: I443bd562bc9df6636bf71a230a05a12e443c6614
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148532
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx 
b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
index 91a027e6ec6b..1c167e61df60 100644
--- a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
@@ -43,27 +43,23 @@
 #include <svtools/languagetoolcfg.hxx>
 #include <tools/color.hxx>
 #include <tools/long.hxx>
+#include <com/sun/star/text/TextMarkupType.hpp>
 #include <com/sun/star/uno/Any.hxx>
 #include <comphelper/propertyvalue.hxx>
 #include <unotools/lingucfg.hxx>
 #include <osl/mutex.hxx>
 #include <rtl/uri.hxx>
 
-using namespace osl;
 using namespace com::sun::star;
 using namespace com::sun::star::beans;
 using namespace com::sun::star::lang;
-using namespace com::sun::star::uno;
 using namespace com::sun::star::linguistic2;
-using namespace linguistic;
-
-#define COL_ORANGE Color(0xD1, 0x68, 0x20)
-
-constexpr OUStringLiteral sDuden = u"duden";
 
 namespace
 {
-Sequence<PropertyValue> lcl_GetLineColorPropertyFromErrorId(const std::string& 
rErrorId)
+constexpr size_t MAX_SUGGESTIONS_SIZE = 10;
+
+PropertyValue lcl_GetLineColorPropertyFromErrorId(const std::string& rErrorId)
 {
     Color aColor;
     if (rErrorId == "TYPOS" || rErrorId == "orth")
@@ -77,10 +73,10 @@ Sequence<PropertyValue> 
lcl_GetLineColorPropertyFromErrorId(const std::string& r
     else
     {
         // Same color is used for other errorId's such as GRAMMAR, TYPOGRAPHY..
+        constexpr Color COL_ORANGE(0xD1, 0x68, 0x20);
         aColor = COL_ORANGE;
     }
-    Sequence<PropertyValue> aProperties{ 
comphelper::makePropertyValue("LineColor", aColor) };
-    return aProperties;
+    return comphelper::makePropertyValue("LineColor", aColor);
 }
 
 OString encodeTextForLanguageTool(const OUString& text)
@@ -115,15 +111,14 @@ enum class HTTP_METHOD
     HTTP_POST
 };
 
-struct curl_cleanup_t
-{
-    void operator()(CURL* p) const { curl_easy_cleanup(p); }
-};
-
-std::string makeHttpRequest_impl(std::string_view aURL, HTTP_METHOD method,
+std::string makeHttpRequest_impl(std::u16string_view aURL, HTTP_METHOD method,
                                  const OString& aPostData, curl_slist* 
pHttpHeader,
                                  tools::Long& nStatusCode)
 {
+    struct curl_cleanup_t
+    {
+        void operator()(CURL* p) const { curl_easy_cleanup(p); }
+    };
     std::unique_ptr<CURL, curl_cleanup_t> curl(curl_easy_init());
     if (!curl)
     {
@@ -139,10 +134,11 @@ std::string makeHttpRequest_impl(std::string_view aURL, 
HTTP_METHOD method,
         + pVersion->version + " " + pVersion->ssl_version);
     (void)curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, useragent.getStr());
 
+    OString aURL8 = OUStringToOString(aURL, RTL_TEXTENCODING_UTF8);
     (void)curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, pHttpHeader);
     (void)curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L);
-    (void)curl_easy_setopt(curl.get(), CURLOPT_URL, aURL.data());
-    (void)curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, CURL_TIMEOUT);
+    (void)curl_easy_setopt(curl.get(), CURLOPT_URL, aURL8.getStr());
+    (void)curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, 10L);
     // (void)curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 1L);
 
     std::string response_body;
@@ -174,7 +170,7 @@ std::string makeHttpRequest_impl(std::string_view aURL, 
HTTP_METHOD method,
     return response_body;
 }
 
-std::string makeDudenHttpRequest(std::string_view aURL, const OString& 
aPostData,
+std::string makeDudenHttpRequest(std::u16string_view aURL, const OString& 
aPostData,
                                  tools::Long& nStatusCode)
 {
     struct curl_slist* pList = nullptr;
@@ -192,7 +188,7 @@ std::string makeDudenHttpRequest(std::string_view aURL, 
const OString& aPostData
     return makeHttpRequest_impl(aURL, HTTP_METHOD::HTTP_POST, aPostData, 
pList, nStatusCode);
 }
 
-std::string makeHttpRequest(std::string_view aURL, HTTP_METHOD method, const 
OString& aPostData,
+std::string makeHttpRequest(std::u16string_view aURL, HTTP_METHOD method, 
const OString& aPostData,
                             tools::Long& nStatusCode)
 {
     OString realPostData(aPostData);
@@ -208,133 +204,98 @@ std::string makeHttpRequest(std::string_view aURL, 
HTTP_METHOD method, const OSt
     return makeHttpRequest_impl(aURL, method, realPostData, nullptr, 
nStatusCode);
 }
 
-void parseDudenResponse(ProofreadingResult& rResult, std::string_view 
aJSONBody)
+template <typename Func>
+Sequence<SingleProofreadingError> parseJson(std::string&& json, std::string 
path, Func f)
 {
-    size_t nSize;
-    int nProposalSize;
+    std::stringstream aStream(std::move(json)); // Optimized in C++20
     boost::property_tree::ptree aRoot;
-    std::stringstream aStream(aJSONBody.data());
     boost::property_tree::read_json(aStream, aRoot);
 
-    const boost::optional<boost::property_tree::ptree&> aPositions
-        = aRoot.get_child_optional("check-positions");
-    if (!aPositions || !(nSize = aPositions.get().size()))
-    {
-        return;
-    }
-
-    Sequence<SingleProofreadingError> aChecks(nSize);
-    auto pChecks = aChecks.getArray();
-    size_t nIndex1 = 0, nIndex2 = 0;
-    auto itPos = aPositions.get().begin();
-    while (itPos != aPositions.get().end())
+    if (auto tree = aRoot.get_child_optional(path))
     {
-        const boost::property_tree::ptree& rTree = itPos->second;
-        const std::string sType = rTree.get<std::string>("type", "");
-        const int nOffset = rTree.get<int>("offset", 0);
-        const int nLength = rTree.get<int>("length", 0);
-
-        pChecks[nIndex1].nErrorStart = nOffset;
-        pChecks[nIndex1].nErrorLength = nLength;
-        pChecks[nIndex1].nErrorType = PROOFREADING_ERROR;
-        //pChecks[nIndex1].aShortComment = ??
-        //pChecks[nIndex1].aFullComment = ??
-        pChecks[nIndex1].aProperties = 
lcl_GetLineColorPropertyFromErrorId(sType);
-
-        const boost::optional<const boost::property_tree::ptree&> aProposals
-            = rTree.get_child_optional("proposals");
-        if (aProposals && (nProposalSize = aProposals.get().size()))
+        Sequence<SingleProofreadingError> aErrors(tree->size());
+        auto it = tree->begin();
+        for (auto& rError : asNonConstRange(aErrors))
         {
-            pChecks[nIndex1].aSuggestions.realloc(std::min(nProposalSize, 
MAX_SUGGESTIONS_SIZE));
+            f(it->second, rError);
+            it++;
+        }
+        return aErrors;
+    }
+    return {};
+}
 
-            nIndex2 = 0;
-            auto itProp = aProposals.get().begin();
-            auto pSuggestions = pChecks[nIndex1].aSuggestions.getArray();
-            while (itProp != aProposals.get().end() && nIndex2 < 
MAX_SUGGESTIONS_SIZE)
+void parseDudenResponse(ProofreadingResult& rResult, std::string&& aJSONBody)
+{
+    rResult.aErrors = parseJson(
+        std::move(aJSONBody), "check-positions",
+        [](const boost::property_tree::ptree& rPos, SingleProofreadingError& 
rError) {
+            rError.nErrorStart = rPos.get<int>("offset", 0);
+            rError.nErrorLength = rPos.get<int>("length", 0);
+            rError.nErrorType = text::TextMarkupType::PROOFREADING;
+            //rError.aShortComment = ??
+            //rError.aFullComment = ??
+            const std::string sType = rPos.get<std::string>("type", {});
+            rError.aProperties = { lcl_GetLineColorPropertyFromErrorId(sType) 
};
+
+            const auto proposals = rPos.get_child_optional("proposals");
+            if (!proposals)
+                return;
+            rError.aSuggestions.realloc(std::min(proposals->size(), 
MAX_SUGGESTIONS_SIZE));
+            auto itProp = proposals->begin();
+            for (auto& rSuggestion : asNonConstRange(rError.aSuggestions))
             {
-                pSuggestions[nIndex2++]
-                    = OStringToOUString(itProp->second.data(), 
RTL_TEXTENCODING_UTF8);
+                rSuggestion = OStringToOUString(itProp->second.data(), 
RTL_TEXTENCODING_UTF8);
                 itProp++;
             }
-        }
-
-        nIndex1++;
-        itPos++;
-    }
-
-    rResult.aErrors = aChecks;
+        });
 }
 
 /*
     rResult is both input and output
     aJSONBody is the response body from the HTTP Request to LanguageTool API
 */
-void parseProofreadingJSONResponse(ProofreadingResult& rResult, 
std::string_view aJSONBody)
+void parseProofreadingJSONResponse(ProofreadingResult& rResult, std::string&& 
aJSONBody)
 {
-    boost::property_tree::ptree root;
-    std::stringstream aStream(aJSONBody.data());
-    boost::property_tree::read_json(aStream, root);
-    boost::property_tree::ptree& matches = root.get_child("matches");
-    size_t matchSize = matches.size();
-
-    if (matchSize <= 0)
-    {
-        return;
-    }
-    Sequence<SingleProofreadingError> aErrors(matchSize);
-    auto pErrors = aErrors.getArray();
-    size_t i = 0;
-    for (auto it1 = matches.begin(); it1 != matches.end(); it1++, i++)
-    {
-        const boost::property_tree::ptree& match = it1->second;
-        int offset = match.get<int>("offset");
-        int length = match.get<int>("length");
-        const std::string shortMessage = match.get<std::string>("message");
-        const std::string message = match.get<std::string>("shortMessage");
-
-        // Parse the error category for Line Color
-        const boost::property_tree::ptree& rule = match.get_child("rule");
-        const boost::property_tree::ptree& ruleCategory = 
rule.get_child("category");
-        const std::string errorCategoryId = 
ruleCategory.get<std::string>("id");
-
-        OUString aShortComment(shortMessage.c_str(), shortMessage.length(), 
RTL_TEXTENCODING_UTF8);
-        OUString aFullComment(message.c_str(), message.length(), 
RTL_TEXTENCODING_UTF8);
-
-        pErrors[i].nErrorStart = offset;
-        pErrors[i].nErrorLength = length;
-        pErrors[i].nErrorType = PROOFREADING_ERROR;
-        pErrors[i].aShortComment = aShortComment;
-        pErrors[i].aFullComment = aFullComment;
-        pErrors[i].aProperties = 
lcl_GetLineColorPropertyFromErrorId(errorCategoryId);
-
-        const boost::property_tree::ptree& replacements = 
match.get_child("replacements");
-        int suggestionSize = replacements.size();
-
-        if (suggestionSize <= 0)
-        {
-            continue;
-        }
-        pErrors[i].aSuggestions.realloc(std::min(suggestionSize, 
MAX_SUGGESTIONS_SIZE));
-        auto pSuggestions = pErrors[i].aSuggestions.getArray();
-        // Limit suggestions to avoid crash on context menu popup:
-        // (soffice:17251): Gdk-CRITICAL **: 17:00:21.277: 
../../../../../gdk/wayland/gdkdisplay-wayland.c:1399: Unable to create Cairo 
image
-        // surface: invalid value (typically too big) for the size of the 
input (surface, pattern, etc.)
-        int j = 0;
-        for (auto it2 = replacements.begin(); it2 != replacements.end() && j < 
MAX_SUGGESTIONS_SIZE;
-             it2++, j++)
-        {
-            const boost::property_tree::ptree& replacement = it2->second;
-            std::string replacementStr = replacement.get<std::string>("value");
-            pSuggestions[j]
-                = OUString(replacementStr.c_str(), replacementStr.length(), 
RTL_TEXTENCODING_UTF8);
-        }
-    }
-    rResult.aErrors = aErrors;
+    rResult.aErrors = parseJson(
+        std::move(aJSONBody), "matches",
+        [](const boost::property_tree::ptree& match, SingleProofreadingError& 
rError) {
+            rError.nErrorStart = match.get<int>("offset", 0);
+            rError.nErrorLength = match.get<int>("length", 0);
+            rError.nErrorType = text::TextMarkupType::PROOFREADING;
+            const std::string shortMessage = match.get<std::string>("message", 
{});
+            const std::string message = match.get<std::string>("shortMessage", 
{});
+
+            rError.aShortComment = OStringToOUString(shortMessage, 
RTL_TEXTENCODING_UTF8);
+            rError.aFullComment = OStringToOUString(message, 
RTL_TEXTENCODING_UTF8);
+
+            // Parse the error category for Line Color
+            std::string errorCategoryId;
+            if (auto rule = match.get_child_optional("rule"))
+                if (auto ruleCategory = rule->get_child_optional("category"))
+                    errorCategoryId = ruleCategory->get<std::string>("id", {});
+            rError.aProperties = { 
lcl_GetLineColorPropertyFromErrorId(errorCategoryId) };
+
+            const auto replacements = match.get_child_optional("replacements");
+            if (!replacements)
+                return;
+            // Limit suggestions to avoid crash on context menu popup:
+            // (soffice:17251): Gdk-CRITICAL **: 17:00:21.277: 
../../../../../gdk/wayland/gdkdisplay-wayland.c:1399: Unable to create Cairo 
image
+            // surface: invalid value (typically too big) for the size of the 
input (surface, pattern, etc.)
+            rError.aSuggestions.realloc(std::min(replacements->size(), 
MAX_SUGGESTIONS_SIZE));
+            auto itRep = replacements->begin();
+            for (auto& rSuggestion : asNonConstRange(rError.aSuggestions))
+            {
+                std::string replacementStr = 
itRep->second.get<std::string>("value", {});
+                rSuggestion = OStringToOUString(replacementStr, 
RTL_TEXTENCODING_UTF8);
+                itRep++;
+            }
+        });
 }
 }
 
 LanguageToolGrammarChecker::LanguageToolGrammarChecker()
-    : mCachedResults(MAX_CACHE_SIZE)
+    : mCachedResults(10)
 {
 }
 
@@ -365,7 +326,7 @@ Sequence<Locale> SAL_CALL 
LanguageToolGrammarChecker::getLocales()
         return m_aSuppLocales;
     }
 
-    OString localeUrl = OUStringToOString(rLanguageOpts.getLocaleListURL(), 
RTL_TEXTENCODING_UTF8);
+    OUString localeUrl = rLanguageOpts.getLocaleListURL();
     if (localeUrl.isEmpty())
     {
         return m_aSuppLocales;
@@ -433,7 +394,7 @@ ProofreadingResult SAL_CALL 
LanguageToolGrammarChecker::doProofreading(
         return xRes;
     }
 
-    OString checkerURL = OUStringToOString(rLanguageOpts.getCheckerURL(), 
RTL_TEXTENCODING_UTF8);
+    OUString checkerURL = rLanguageOpts.getCheckerURL();
     if (checkerURL.isEmpty())
     {
         return xRes;
@@ -463,7 +424,8 @@ ProofreadingResult SAL_CALL 
LanguageToolGrammarChecker::doProofreading(
 
     OString langTag(LanguageTag::convertToBcp47(aLocale, false).toUtf8());
     OString postData = encodeTextForLanguageTool(aText);
-    if (rLanguageOpts.getRestProtocol() == sDuden)
+    const bool bDudenProtocol = rLanguageOpts.getRestProtocol() == "duden";
+    if (bDudenProtocol)
     {
         std::stringstream aStream;
         boost::property_tree::ptree aTree;
@@ -488,7 +450,7 @@ ProofreadingResult SAL_CALL 
LanguageToolGrammarChecker::doProofreading(
 
     tools::Long http_code = 0;
     std::string response_body;
-    if (rLanguageOpts.getRestProtocol() == sDuden)
+    if (bDudenProtocol)
         response_body = makeDudenHttpRequest(checkerURL, postData, http_code);
     else
         response_body = makeHttpRequest(checkerURL, HTTP_METHOD::HTTP_POST, 
postData, http_code);
@@ -503,13 +465,13 @@ ProofreadingResult SAL_CALL 
LanguageToolGrammarChecker::doProofreading(
         return xRes;
     }
 
-    if (rLanguageOpts.getRestProtocol() == sDuden)
+    if (bDudenProtocol)
     {
-        parseDudenResponse(xRes, response_body);
+        parseDudenResponse(xRes, std::move(response_body));
     }
     else
     {
-        parseProofreadingJSONResponse(xRes, response_body);
+        parseProofreadingJSONResponse(xRes, std::move(response_body));
     }
     // cache the result
     mCachedResults.insert(std::make_pair(postData, xRes.aErrors));
diff --git a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx 
b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
index 7827560045ce..93d2c84c612d 100644
--- a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
@@ -30,12 +30,6 @@
 #include <o3tl/lru_map.hxx>
 #include <tools/long.hxx>
 
-// Magical numbers
-#define MAX_CACHE_SIZE 10
-#define MAX_SUGGESTIONS_SIZE 10
-#define PROOFREADING_ERROR 2
-#define CURL_TIMEOUT 10L
-
 class LanguageToolGrammarChecker
     : public cppu::WeakImplHelper<css::linguistic2::XProofreader, 
css::lang::XInitialization,
                                   css::lang::XServiceInfo, 
css::lang::XServiceDisplayName>

Reply via email to