summaryrefslogtreecommitdiff
path: root/lingucomponent
diff options
context:
space:
mode:
authorMert Tumer <mert.tumer@collabora.com>2022-05-10 13:06:06 +0300
committerMert Tumer <mert.tumer@collabora.com>2022-06-14 16:13:22 +0200
commit7686dceb70881e8a3c4ac7f18ced15e5202de8c7 (patch)
treeb73601bbbc496eef7796349e224eadcd702d012a /lingucomponent
parentb5e01104fc7df9b715a70bc1cc86ad1af13b51fa (diff)
LanguageTool Grammar Checker implementation
Signed-off-by: Mert Tumer <mert.tumer@collabora.com> Change-Id: I275cbea668afc5beb5147370119631df8b6a2d46 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135464
Diffstat (limited to 'lingucomponent')
-rw-r--r--lingucomponent/Library_LanguageTool.mk48
-rw-r--r--lingucomponent/Module_lingucomponent.mk1
-rw-r--r--lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu30
-rw-r--r--lingucomponent/source/spellcheck/languagetool/LanguageTool.component26
-rw-r--r--lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx394
-rw-r--r--lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx91
6 files changed, 590 insertions, 0 deletions
diff --git a/lingucomponent/Library_LanguageTool.mk b/lingucomponent/Library_LanguageTool.mk
new file mode 100644
index 000000000000..98ff7cf7edf2
--- /dev/null
+++ b/lingucomponent/Library_LanguageTool.mk
@@ -0,0 +1,48 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,LanguageTool))
+
+$(eval $(call gb_Library_set_componentfile,LanguageTool,lingucomponent/source/spellcheck/languagetool/LanguageTool,services))
+
+$(eval $(call gb_Library_set_include,LanguageTool,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/lingucomponent/source/lingutil \
+))
+
+$(eval $(call gb_Library_use_sdk_api,LanguageTool))
+
+$(eval $(call gb_Library_use_libraries,LanguageTool,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ i18nlangtag \
+ svt \
+ lng \
+ sal \
+ tl \
+ utl \
+))
+
+$(eval $(call gb_Library_use_static_libraries,LanguageTool,\
+ ulingu \
+))
+
+$(eval $(call gb_Library_use_externals,LanguageTool,\
+ boost_headers \
+ icuuc \
+ curl \
+))
+
+$(eval $(call gb_Library_add_exception_objects,LanguageTool,\
+ lingucomponent/source/spellcheck/languagetool/languagetoolimp \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/lingucomponent/Module_lingucomponent.mk b/lingucomponent/Module_lingucomponent.mk
index 2bde5d5391e5..c77a7c5df0a0 100644
--- a/lingucomponent/Module_lingucomponent.mk
+++ b/lingucomponent/Module_lingucomponent.mk
@@ -18,6 +18,7 @@ $(eval $(call gb_Module_add_targets,lingucomponent,\
Library_spell \
StaticLibrary_ulingu \
Library_numbertext \
+ Library_LanguageTool \
))
# vim: set noet sw=4 ts=4:
diff --git a/lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu b/lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu
new file mode 100644
index 000000000000..ce3d6033d0c2
--- /dev/null
+++ b/lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu
@@ -0,0 +1,30 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<oor:component-data oor:name="Linguistic" oor:package="org.openoffice.Office" xmlns:install="http://openoffice.org/2004/installation" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <node oor:name="ServiceManager">
+ <node oor:name="GrammarCheckers">
+ <node oor:name="org.openoffice.lingu.LanguageToolGrammarChecker" oor:op="fuse">
+ <prop oor:name="Locales" oor:type="oor:string-list">
+ <value>ar ast-ES be-BY br-FR ca-ES ca-ES-valencia zh-CN da-DK nl nl-BE en en-AU en-CA en-CA en-GB en-NZ en-ZA en-US fr gl-ES de de-AT de-DE de-DE de-CH el-GR ga-IE it ja-JP km-KH nb no fa pl-PL pt pt-AO pt-BR pt-MZ pt-PT ro-RO ru-RU de-DE-x-simple-language sk-SK sl-SI es es-AR sv tl-PH ta-IN uk-UA</value>
+ </prop>
+ </node>
+ </node>
+ </node>
+</oor:component-data>
+
diff --git a/lingucomponent/source/spellcheck/languagetool/LanguageTool.component b/lingucomponent/source/spellcheck/languagetool/LanguageTool.component
new file mode 100644
index 000000000000..9f7eb3d08789
--- /dev/null
+++ b/lingucomponent/source/spellcheck/languagetool/LanguageTool.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.openoffice.lingu.LanguageToolGrammarChecker"
+ constructor="lingucomponent_LanguageToolGrammarChecker_get_implementation" single-instance="true">
+ <service name="com.sun.star.linguistic2.Proofreader"/>
+ </implementation>
+</component>
diff --git a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
new file mode 100644
index 000000000000..5479e67e82c4
--- /dev/null
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
@@ -0,0 +1,394 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include "languagetoolimp.hxx"
+
+#include <i18nlangtag/languagetag.hxx>
+#include <svtools/strings.hrc>
+#include <unotools/resmgr.hxx>
+
+#include <vector>
+#include <set>
+#include <string.h>
+
+#include <curl/curl.h>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <algorithm>
+#include <string_view>
+#include <svtools/languagetoolcfg.hxx>
+#include <tools/color.hxx>
+#include <tools/long.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <unotools/lingucfg.hxx>
+#include <osl/mutex.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)
+
+namespace
+{
+PropertyValue lcl_MakePropertyValue(const OUString& rName, uno::Any& rValue)
+{
+ return PropertyValue(rName, -1, rValue, css::beans::PropertyState_DIRECT_VALUE);
+}
+
+Sequence<PropertyValue> lcl_GetLineColorPropertyFromErrorId(const std::string& rErrorId)
+{
+ uno::Any aColor;
+ if (rErrorId == "TYPOS")
+ {
+ aColor <<= COL_LIGHTRED;
+ }
+ else if (rErrorId == "STYLE")
+ {
+ aColor <<= COL_LIGHTBLUE;
+ }
+ else
+ {
+ // Same color is used for other errorId's such as GRAMMAR, TYPOGRAPHY..
+ aColor <<= COL_ORANGE;
+ }
+ Sequence<PropertyValue> aProperties{ lcl_MakePropertyValue("LineColor", aColor) };
+ return aProperties;
+}
+}
+
+LanguageToolGrammarChecker::LanguageToolGrammarChecker()
+ : mCachedResults(MAX_CACHE_SIZE)
+{
+}
+
+LanguageToolGrammarChecker::~LanguageToolGrammarChecker() {}
+
+sal_Bool SAL_CALL LanguageToolGrammarChecker::isSpellChecker() { return false; }
+
+sal_Bool SAL_CALL LanguageToolGrammarChecker::hasLocale(const Locale& rLocale)
+{
+ bool bRes = false;
+ if (!m_aSuppLocales.hasElements())
+ getLocales();
+
+ for (auto const& suppLocale : std::as_const(m_aSuppLocales))
+ {
+ if (rLocale == suppLocale)
+ {
+ bRes = true;
+ break;
+ }
+ }
+
+ return bRes;
+}
+
+Sequence<Locale> SAL_CALL LanguageToolGrammarChecker::getLocales()
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ if (m_aSuppLocales.hasElements())
+ return m_aSuppLocales;
+
+ SvtLinguConfig aLinguCfg;
+ uno::Sequence< OUString > aLocaleList;
+ aLinguCfg.GetLocaleListFor( "GrammarCheckers",
+ "org.openoffice.lingu.LanguageToolGrammarChecker", aLocaleList );
+
+ auto nLength = aLocaleList.getLength();
+ m_aSuppLocales.realloc(nLength);
+ auto pArray = m_aSuppLocales.getArray();
+ auto pLocaleList = aLocaleList.getArray();
+
+ for (auto i = 0; i < nLength; i++)
+ {
+ pArray[i] = LanguageTag::convertToLocale(pLocaleList[i]);
+ }
+
+ return m_aSuppLocales;
+}
+
+// Callback to get the response data from server.
+static size_t WriteCallback(void* ptr, size_t size, size_t nmemb, void* userp)
+{
+ if (!userp)
+ return 0;
+
+ std::string* response = static_cast<std::string*>(userp);
+ size_t real_size = size * nmemb;
+ response->append(static_cast<char*>(ptr), real_size);
+ return real_size;
+}
+
+ProofreadingResult SAL_CALL LanguageToolGrammarChecker::doProofreading(
+ const OUString& aDocumentIdentifier, const OUString& aText, const Locale& aLocale,
+ sal_Int32 nStartOfSentencePosition, sal_Int32 nSuggestedBehindEndOfSentencePosition,
+ const Sequence<PropertyValue>& aProperties)
+{
+ // ProofreadingResult declared here instead of parseHttpJSONResponse because of the early exists.
+ ProofreadingResult xRes;
+ xRes.aDocumentIdentifier = aDocumentIdentifier;
+ xRes.aText = aText;
+ xRes.aLocale = aLocale;
+ xRes.nStartOfSentencePosition = nStartOfSentencePosition;
+ xRes.nBehindEndOfSentencePosition = nSuggestedBehindEndOfSentencePosition;
+ xRes.aProperties = Sequence<PropertyValue>();
+ xRes.xProofreader = this;
+ xRes.aErrors = Sequence<SingleProofreadingError>();
+
+ if (aText.isEmpty())
+ {
+ return xRes;
+ }
+
+ if (nStartOfSentencePosition != 0)
+ {
+ return xRes;
+ }
+
+ xRes.nStartOfNextSentencePosition = aText.getLength();
+
+ SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+ if (rLanguageOpts.getEnabled() == false)
+ {
+ return xRes;
+ }
+
+ OString checkerURL = OUStringToOString(rLanguageOpts.getCheckerURL(), RTL_TEXTENCODING_UTF8);
+ if (checkerURL.isEmpty())
+ {
+ return xRes;
+ }
+
+ if (aProperties.getLength() > 0 && aProperties[0].Name == "Update")
+ {
+ // locale changed
+ xRes.aText = "";
+ return xRes;
+ }
+
+ sal_Int32 spaceIndex = std::min(xRes.nStartOfNextSentencePosition, aText.getLength() - 1);
+ while (spaceIndex < aText.getLength() && aText[spaceIndex] == ' ')
+ {
+ xRes.nStartOfNextSentencePosition += 1;
+ spaceIndex = xRes.nStartOfNextSentencePosition;
+ }
+ if (xRes.nStartOfNextSentencePosition == nSuggestedBehindEndOfSentencePosition
+ && spaceIndex < aText.getLength())
+ {
+ xRes.nStartOfNextSentencePosition
+ = std::min(nSuggestedBehindEndOfSentencePosition + 1, aText.getLength());
+ }
+ xRes.nBehindEndOfSentencePosition
+ = std::min(xRes.nStartOfNextSentencePosition, aText.getLength());
+
+ auto cachedResult = mCachedResults.find(aText);
+ if (cachedResult != mCachedResults.end())
+ {
+ xRes.aErrors = cachedResult->second;
+ return xRes;
+ }
+
+ tools::Long http_code = 0;
+ OUString langTag(aLocale.Language + "-" + aLocale.Country);
+ OString postData(OUStringToOString(
+ OUStringConcatenation("text=" + aText + "&language=" + langTag), RTL_TEXTENCODING_UTF8));
+ const std::string response_body
+ = makeHttpRequest(checkerURL, HTTP_METHOD::HTTP_POST, postData, http_code);
+
+ if (http_code != 200)
+ {
+ return xRes;
+ }
+
+ if (response_body.length() <= 0)
+ {
+ return xRes;
+ }
+
+ parseProofreadingJSONResponse(xRes, response_body);
+ // cache the result
+ mCachedResults.insert(
+ std::pair<OUString, Sequence<SingleProofreadingError>>(aText, xRes.aErrors));
+ return xRes;
+}
+
+/*
+ rResult is both input and output
+ aJSONBody is the response body from the HTTP Request to LanguageTool API
+*/
+void LanguageToolGrammarChecker::parseProofreadingJSONResponse(ProofreadingResult& rResult,
+ std::string_view 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;
+}
+
+std::string LanguageToolGrammarChecker::makeHttpRequest(std::string_view aURL, HTTP_METHOD method,
+ const OString& aPostData,
+ tools::Long& nStatusCode)
+{
+ std::unique_ptr<CURL, std::function<void(CURL*)>> curl(curl_easy_init(),
+ [](CURL* p) { curl_easy_cleanup(p); });
+ if (!curl)
+ return {}; // empty string
+
+ bool isPremium = false;
+ SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+ OString apiKey = OUStringToOString(rLanguageOpts.getApiKey(), RTL_TEXTENCODING_UTF8);
+ OString username = OUStringToOString(rLanguageOpts.getUsername(), RTL_TEXTENCODING_UTF8);
+ OString premiumPostData;
+ if (!apiKey.isEmpty() && !username.isEmpty())
+ {
+ isPremium = true;
+ }
+
+ std::string response_body;
+ curl_easy_setopt(curl.get(), CURLOPT_URL, aURL.data());
+
+ curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L);
+ // curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 1L);
+
+ curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, static_cast<void*>(&response_body));
+ curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, false);
+ curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, false);
+ curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, CURL_TIMEOUT);
+
+ if (method == HTTP_METHOD::HTTP_POST)
+ {
+ curl_easy_setopt(curl.get(), CURLOPT_POST, 1L);
+ if (isPremium == false)
+ {
+ curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, aPostData.getStr());
+ }
+ else
+ {
+ premiumPostData = aPostData + "&username=" + username + "&apiKey=" + apiKey;
+ curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, premiumPostData.getStr());
+ }
+ }
+
+ /*CURLcode cc = */
+ curl_easy_perform(curl.get());
+ curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &nStatusCode);
+ return response_body;
+}
+
+void SAL_CALL LanguageToolGrammarChecker::ignoreRule(const OUString& /*aRuleIdentifier*/,
+ const Locale& /*aLocale*/
+)
+{
+}
+void SAL_CALL LanguageToolGrammarChecker::resetIgnoreRules() {}
+
+OUString SAL_CALL LanguageToolGrammarChecker::getServiceDisplayName(const Locale& rLocale)
+{
+ std::locale loc(Translate::Create("svt", LanguageTag(rLocale)));
+ return Translate::get(STR_DESCRIPTION_LANGUAGETOOL, loc);
+}
+
+OUString SAL_CALL LanguageToolGrammarChecker::getImplementationName()
+{
+ return "org.openoffice.lingu.LanguageToolGrammarChecker";
+}
+
+sal_Bool SAL_CALL LanguageToolGrammarChecker::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence<OUString> SAL_CALL LanguageToolGrammarChecker::getSupportedServiceNames()
+{
+ return { SN_GRAMMARCHECKER };
+}
+
+void SAL_CALL LanguageToolGrammarChecker::initialize(const Sequence<Any>& /*rArguments*/) {}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+lingucomponent_LanguageToolGrammarChecker_get_implementation(
+ css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(static_cast<cppu::OWeakObject*>(new LanguageToolGrammarChecker()));
+} \ No newline at end of file
diff --git a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
new file mode 100644
index 000000000000..e0dadfecab68
--- /dev/null
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceDisplayName.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/linguistic2/XProofreader.hpp>
+#include <com/sun/star/linguistic2/ProofreadingResult.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <linguistic/misc.hxx>
+#include <string_view>
+#include <o3tl/lru_map.hxx>
+#include <tools/long.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::linguistic2;
+
+// Magical numbers
+#define MAX_CACHE_SIZE 10
+#define MAX_SUGGESTIONS_SIZE 10
+#define PROOFREADING_ERROR 2
+#define CURL_TIMEOUT 10L
+
+enum class HTTP_METHOD
+{
+ HTTP_GET,
+ HTTP_POST
+};
+
+class LanguageToolGrammarChecker
+ : public cppu::WeakImplHelper<XProofreader, XInitialization, XServiceInfo, XServiceDisplayName>
+{
+ Sequence<Locale> m_aSuppLocales;
+ o3tl::lru_map<OUString, Sequence<SingleProofreadingError>> mCachedResults;
+ LanguageToolGrammarChecker(const LanguageToolGrammarChecker&) = delete;
+ LanguageToolGrammarChecker& operator=(const LanguageToolGrammarChecker&) = delete;
+ static void parseProofreadingJSONResponse(ProofreadingResult& rResult,
+ std::string_view aJSONBody);
+ static std::string makeHttpRequest(std::string_view aURL, HTTP_METHOD method,
+ const OString& aPostData, tools::Long& nStatusCode);
+
+public:
+ LanguageToolGrammarChecker();
+ virtual ~LanguageToolGrammarChecker() override;
+
+ // XSupportedLocales
+ virtual Sequence<Locale> SAL_CALL getLocales() override;
+ virtual sal_Bool SAL_CALL hasLocale(const Locale& rLocale) override;
+
+ // XProofReader
+ virtual sal_Bool SAL_CALL isSpellChecker() override;
+ virtual ProofreadingResult SAL_CALL doProofreading(
+ const OUString& aDocumentIdentifier, const OUString& aText, const Locale& aLocale,
+ sal_Int32 nStartOfSentencePosition, sal_Int32 nSuggestedBehindEndOfSentencePosition,
+ const Sequence<PropertyValue>& aProperties) override;
+
+ virtual void SAL_CALL ignoreRule(const OUString& aRuleIdentifier,
+ const Locale& aLocale) override;
+ virtual void SAL_CALL resetIgnoreRules() override;
+
+ // XServiceDisplayName
+ virtual OUString SAL_CALL getServiceDisplayName(const Locale& rLocale) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const Sequence<Any>& rArguments) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
+ virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+}; \ No newline at end of file