summaryrefslogtreecommitdiffstats
path: root/helpcompiler/source/HelpIndexer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'helpcompiler/source/HelpIndexer.cxx')
-rw-r--r--helpcompiler/source/HelpIndexer.cxx210
1 files changed, 210 insertions, 0 deletions
diff --git a/helpcompiler/source/HelpIndexer.cxx b/helpcompiler/source/HelpIndexer.cxx
new file mode 100644
index 0000000000..65e46743b4
--- /dev/null
+++ b/helpcompiler/source/HelpIndexer.cxx
@@ -0,0 +1,210 @@
+/* -*- 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/.
+ */
+
+#include <helpcompiler/HelpIndexer.hxx>
+
+#include <rtl/string.hxx>
+#include <rtl/uri.hxx>
+#include <o3tl/runtimetooustring.hxx>
+#include <osl/file.hxx>
+#include <osl/thread.h>
+#include <o3tl/string_view.hxx>
+#include <memory>
+#include <utility>
+
+#include "LuceneHelper.hxx"
+#include <CLucene.h>
+#include <CLucene/analysis/LanguageBasedAnalyzer.h>
+
+#if defined _WIN32
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <prewin.h>
+#include <postwin.h>
+#endif
+
+using namespace lucene::document;
+
+HelpIndexer::HelpIndexer(OUString lang, OUString module,
+ std::u16string_view srcDir, std::u16string_view outDir)
+ : d_lang(std::move(lang)), d_module(std::move(module))
+{
+ d_indexDir = outDir + OUStringChar('/') + d_module + ".idxl";
+ osl_getAbsoluteFileURL(nullptr, d_indexDir.pData, &d_indexDir.pData);
+ d_captionDir = OUString::Concat(srcDir) + "/caption";
+ osl_getAbsoluteFileURL(nullptr, d_captionDir.pData, &d_captionDir.pData);
+ d_contentDir = OUString::Concat(srcDir) + "/content";
+ osl_getAbsoluteFileURL(nullptr, d_contentDir.pData, &d_contentDir.pData);
+}
+
+#if defined _WIN32
+namespace
+{
+template <class Constructor>
+auto TryWithUnicodePathWorkaround(const OUString& ustrPath, const Constructor& constructor)
+{
+ const rtl_TextEncoding eThreadEncoding = osl_getThreadTextEncoding();
+ OString sPath = OUStringToOString(ustrPath, eThreadEncoding);
+ try
+ {
+ // First try path in thread encoding (ACP in case of Windows).
+ return constructor(sPath);
+ }
+ catch (const CLuceneError&)
+ {
+ // Maybe the path contains characters not representable in ACP. There's no API in lucene
+ // that takes Unicode strings (they take 8-bit strings, and pass them to CRT library
+ // functions without conversion).
+
+ // For a workaround, try short name, which should only contain ASCII characters. Would
+ // not help (i.e., would return original long name) if short (8.3) file name creation is
+ // disabled in OS or volume settings.
+ wchar_t buf[32767];
+ if (GetShortPathNameW(o3tl::toW(ustrPath.getStr()), buf, std::size(buf)) == 0)
+ throw;
+ sPath = OUStringToOString(o3tl::toU(buf), eThreadEncoding);
+ return constructor(sPath);
+ }
+}
+}
+#endif
+
+bool HelpIndexer::indexDocuments()
+{
+ if (!scanForFiles())
+ return false;
+
+ try
+ {
+ std::u16string_view sLang = o3tl::getToken(d_lang, 0, '-');
+ bool bUseCJK = sLang == u"ja" || sLang == u"ko" || sLang == u"zh";
+
+ // Construct the analyzer appropriate for the given language
+ std::unique_ptr<lucene::analysis::Analyzer> analyzer;
+ if (bUseCJK)
+ analyzer.reset(new lucene::analysis::LanguageBasedAnalyzer(L"cjk"));
+ else
+ analyzer.reset(new lucene::analysis::standard::StandardAnalyzer());
+
+ OUString ustrSystemPath;
+ osl::File::getSystemPathFromFileURL(d_indexDir, ustrSystemPath);
+
+#if defined _WIN32
+ // Make sure the path exists, or GetShortPathNameW (if attempted) will fail.
+ osl::Directory::createPath(d_indexDir);
+ auto writer = TryWithUnicodePathWorkaround(ustrSystemPath, [&analyzer](const OString& s) {
+ return std::make_unique<lucene::index::IndexWriter>(s.getStr(), analyzer.get(), true);
+ });
+#else
+ OString indexDirStr = OUStringToOString(ustrSystemPath, osl_getThreadTextEncoding());
+ auto writer = std::make_unique<lucene::index::IndexWriter>(indexDirStr.getStr(),
+ analyzer.get(), true);
+#endif
+
+#ifndef SYSTEM_CLUCENE
+ // avoid random values in index file, making help indices reproducible
+ writer->setSegmentInfoStartVersion(0);
+#endif
+
+ //Double limit of tokens allowed, otherwise we'll get a too-many-tokens
+ //exception for ja help. Could alternative ignore the exception and get
+ //truncated results as per java-Lucene apparently
+ writer->setMaxFieldLength(lucene::index::IndexWriter::DEFAULT_MAX_FIELD_LENGTH*2);
+
+ // Index the identified help files
+ Document doc;
+ for (auto const& elem : d_files)
+ {
+ helpDocument(elem, &doc);
+ writer->addDocument(&doc);
+ doc.clear();
+ }
+
+ // Optimize the index
+ writer->optimize();
+ }
+ catch (CLuceneError &e)
+ {
+ d_error = o3tl::runtimeToOUString(e.what());
+ return false;
+ }
+
+ return true;
+}
+
+
+bool HelpIndexer::scanForFiles() {
+ if (!scanForFiles(d_contentDir)) {
+ return false;
+ }
+ if (!scanForFiles(d_captionDir)) {
+ return false;
+ }
+ return true;
+}
+
+bool HelpIndexer::scanForFiles(OUString const & path) {
+
+ osl::Directory dir(path);
+ if (osl::FileBase::E_None != dir.open()) {
+ d_error = "Error reading directory " + path;
+ return false;
+ }
+
+ osl::DirectoryItem item;
+ osl::FileStatus fileStatus(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Type);
+ while (dir.getNextItem(item) == osl::FileBase::E_None) {
+ item.getFileStatus(fileStatus);
+ if (fileStatus.getFileType() == osl::FileStatus::Regular) {
+ d_files.insert(fileStatus.getFileName());
+ }
+ }
+
+ return true;
+}
+
+void HelpIndexer::helpDocument(OUString const & fileName, Document *doc) const {
+ // Add the help path as an indexed, untokenized field.
+
+ OUString path = "#HLP#" + d_module + "/" + fileName;
+ std::vector<TCHAR> aPath(OUStringToTCHARVec(path));
+ doc->add(*_CLNEW Field(_T("path"), aPath.data(), int(Field::STORE_YES) | int(Field::INDEX_UNTOKENIZED)));
+
+ OUString sEscapedFileName =
+ rtl::Uri::encode(fileName,
+ rtl_UriCharClassUric, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8);
+
+ // Add the caption as a field.
+ OUString captionPath = d_captionDir + "/" + sEscapedFileName;
+ doc->add(*_CLNEW Field(_T("caption"), helpFileReader(captionPath), int(Field::STORE_NO) | int(Field::INDEX_TOKENIZED)));
+
+ // Add the content as a field.
+ OUString contentPath = d_contentDir + "/" + sEscapedFileName;
+ doc->add(*_CLNEW Field(_T("content"), helpFileReader(contentPath), int(Field::STORE_NO) | int(Field::INDEX_TOKENIZED)));
+}
+
+lucene::util::Reader *HelpIndexer::helpFileReader(OUString const & path) {
+ osl::File file(path);
+ if (osl::FileBase::E_None == file.open(osl_File_OpenFlag_Read)) {
+ file.close();
+ OUString ustrSystemPath;
+ osl::File::getSystemPathFromFileURL(path, ustrSystemPath);
+#if defined _WIN32
+ return TryWithUnicodePathWorkaround(ustrSystemPath, [](const OString& s) {
+ return _CLNEW lucene::util::FileReader(s.getStr(), "UTF-8");
+ });
+#else
+ OString pathStr = OUStringToOString(ustrSystemPath, osl_getThreadTextEncoding());
+ return _CLNEW lucene::util::FileReader(pathStr.getStr(), "UTF-8");
+#endif
+ } else {
+ return _CLNEW lucene::util::StringReader(L"");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */