diff options
Diffstat (limited to 'lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx')
-rw-r--r-- | lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx | 228 |
1 files changed, 95 insertions, 133 deletions
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)); |