sw/qa/extras/indexing/SearchResultLocatorTest.cxx | 36 +++++++- sw/source/core/inc/SearchResultLocator.hxx | 15 ++- sw/source/core/model/SearchResultLocator.cxx | 98 +++++++++++++++------- 3 files changed, 112 insertions(+), 37 deletions(-)
New commits: commit eac288d02cafc49c5a14fa27bb449c33eb4b1803 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Mon Sep 6 14:17:01 2021 +0900 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Wed Sep 8 06:57:23 2021 +0200 indexing: support JSON and XML as input for SearchResultLocator JSON is much easier to deal with when using REST and javascript, so support both. Change-Id: I61035452d9a7ba889ac355a42201d79b9fafec6f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121742 Tested-by: Tomaž Vajngerl <qui...@gmail.com> Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/sw/qa/extras/indexing/SearchResultLocatorTest.cxx b/sw/qa/extras/indexing/SearchResultLocatorTest.cxx index c740f982b2c3..fd9e8c4f3827 100644 --- a/sw/qa/extras/indexing/SearchResultLocatorTest.cxx +++ b/sw/qa/extras/indexing/SearchResultLocatorTest.cxx @@ -27,12 +27,14 @@ private: public: void testSearchResultLocator(); - void testSearchResultLocatorUsingPayload(); + void testSearchResultLocatorUsingXmlPayload(); + void testSearchResultLocatorUsingJsonPayload(); void testSearchResultLocatorForSdrObjects(); CPPUNIT_TEST_SUITE(SearchResultLocatorTest); CPPUNIT_TEST(testSearchResultLocator); - CPPUNIT_TEST(testSearchResultLocatorUsingPayload); + CPPUNIT_TEST(testSearchResultLocatorUsingXmlPayload); + CPPUNIT_TEST(testSearchResultLocatorUsingJsonPayload); CPPUNIT_TEST(testSearchResultLocatorForSdrObjects); CPPUNIT_TEST_SUITE_END(); }; @@ -76,7 +78,7 @@ void SearchResultLocatorTest::testSearchResultLocator() #endif } -void SearchResultLocatorTest::testSearchResultLocatorUsingPayload() +void SearchResultLocatorTest::testSearchResultLocatorUsingXmlPayload() { if (!IsDefaultDPI()) return; @@ -104,6 +106,34 @@ void SearchResultLocatorTest::testSearchResultLocatorUsingPayload() #endif } +void SearchResultLocatorTest::testSearchResultLocatorUsingJsonPayload() +{ + if (!IsDefaultDPI()) + return; + + SwDoc* pDoc = createDoc("IndexingExport_VariousParagraphs.odt"); + CPPUNIT_ASSERT(pDoc); + + sw::search::SearchResultLocator aLocator(pDoc); + OString payload = "[" + "{ \"type\" : 1, \"index\" : 14 }" + "]"; + + sw::search::LocationResult aResult = aLocator.findForPayload(payload.getStr()); + CPPUNIT_ASSERT_EQUAL(size_t(1), aResult.maRectangles.size()); + + // skip asserting exact values for macOS and Windows because of + // inconsistent results +#if !defined(_WIN32) && !defined(MACOSX) + auto aRectangle = aResult.maRectangles[0]; + CPPUNIT_ASSERT_DOUBLES_EQUAL(1418.0, aRectangle.getMinX(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(4444.0, aRectangle.getMinY(), 1e-4); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(9638.0, aRectangle.getWidth(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(276.0, aRectangle.getHeight(), 1e-4); +#endif +} + void SearchResultLocatorTest::testSearchResultLocatorForSdrObjects() { if (!IsDefaultDPI()) diff --git a/sw/source/core/inc/SearchResultLocator.hxx b/sw/source/core/inc/SearchResultLocator.hxx index 7dac632ae58f..fb46c85253eb 100644 --- a/sw/source/core/inc/SearchResultLocator.hxx +++ b/sw/source/core/inc/SearchResultLocator.hxx @@ -18,20 +18,20 @@ namespace sw::search { enum class NodeType { - Undefined, - WriterNode, - SdrObject + Undefined = 0, + WriterNode = 1, + SdrObject = 2 }; struct SearchIndexData { NodeType meType = NodeType::Undefined; - sal_uInt32 mnNodeIndex = 0; + sal_Int32 mnNodeIndex = 0; OUString maObjectName; SearchIndexData() {} - SearchIndexData(NodeType eType, sal_uInt32 nNodeIndex, OUString const& aObjectName = OUString()) + SearchIndexData(NodeType eType, sal_Int32 nNodeIndex, OUString const& aObjectName = OUString()) : meType(eType) , mnNodeIndex(nNodeIndex) , maObjectName(aObjectName) @@ -50,6 +50,10 @@ class SW_DLLPUBLIC SearchResultLocator SwDoc* mpDocument; void findOne(LocationResult& rResult, SearchIndexData const& rSearchIndexData); + static bool tryParseJSON(const char* pPayload, + std::vector<sw::search::SearchIndexData>& rDataVector); + static bool tryParseXML(const char* pPayload, + std::vector<sw::search::SearchIndexData>& rDataVector); public: SearchResultLocator(SwDoc* pDoc) @@ -58,7 +62,6 @@ public: } LocationResult find(std::vector<SearchIndexData> const& rSearchIndexDataVector); - LocationResult findForPayload(const char* pPayload); }; diff --git a/sw/source/core/model/SearchResultLocator.cxx b/sw/source/core/model/SearchResultLocator.cxx index a0e405b2ed33..089859683cb4 100644 --- a/sw/source/core/model/SearchResultLocator.cxx +++ b/sw/source/core/model/SearchResultLocator.cxx @@ -20,6 +20,8 @@ #include <tools/XmlWalker.hxx> #include <tools/stream.hxx> +#include <boost/property_tree/json_parser.hpp> + #include <svx/svdpage.hxx> #include <svx/svdobj.hxx> @@ -30,7 +32,7 @@ void SearchResultLocator::findOne(LocationResult& rResult, SearchIndexData const if (rSearchIndexData.meType == NodeType::WriterNode) { SwNodes const& rNodes = mpDocument->GetNodes(); - if (rSearchIndexData.mnNodeIndex >= rNodes.Count()) + if (rSearchIndexData.mnNodeIndex >= sal_Int32(rNodes.Count())) return; SwNode* pNode = rNodes[rSearchIndexData.mnNodeIndex]; @@ -85,51 +87,91 @@ LocationResult SearchResultLocator::find(std::vector<SearchIndexData> const& rSe return aResult; } -LocationResult SearchResultLocator::findForPayload(const char* pPayload) +/** Trying to parse the payload as JSON + * + * Returns true if parsing was successful and the payload was identified as JSON, else false + */ +bool SearchResultLocator::tryParseJSON(const char* pPayload, + std::vector<sw::search::SearchIndexData>& rDataVector) { - LocationResult aResult; + boost::property_tree::ptree aTree; + std::stringstream aStream(pPayload); + try + { + boost::property_tree::read_json(aStream, aTree); + } + catch (const boost::property_tree::json_parser_error& /*exception*/) + { + return false; + } + for (auto& rEachNode : boost::make_iterator_range(aTree.equal_range(""))) + { + auto const& rEach = rEachNode.second; + + sal_Int32 nType = rEach.get<sal_Int32>("type", 0); + sal_Int32 nIndex = rEach.get<sal_Int32>("index", -1); + + // Don't add search data elements that don't have valid data + if (nType > 0 && nIndex >= 0) + rDataVector.emplace_back(sw::search::NodeType(nType), nIndex); + } + + return true; +} + +/** Trying to parse the payload as XML + * + * Returns true if parsing was successful and the payload was identified as XML, else false + */ +bool SearchResultLocator::tryParseXML(const char* pPayload, + std::vector<sw::search::SearchIndexData>& rDataVector) +{ const OString aPayloadString(pPayload); SvMemoryStream aStream(const_cast<char*>(aPayloadString.getStr()), aPayloadString.getLength(), StreamMode::READ); + tools::XmlWalker aWalker; if (!aWalker.open(&aStream)) - return aResult; + return false; - if (aWalker.name() == "indexing") + if (aWalker.name() != "indexing") + return true; + + aWalker.children(); + while (aWalker.isValid()) { - std::vector<sw::search::SearchIndexData> aDataVector; - aWalker.children(); - while (aWalker.isValid()) + if (aWalker.name() == "paragraph") { - if (aWalker.name() == "paragraph") - { - OString sType = aWalker.attribute("type"); - OString sIndex = aWalker.attribute("index"); + OString sType = aWalker.attribute("type"); + OString sIndex = aWalker.attribute("index"); - if (!sType.isEmpty() && !sIndex.isEmpty()) - { - sw::search::SearchIndexData aData; - aData.mnNodeIndex = sIndex.toInt32(); - aData.meType = sw::search::NodeType(sType.toInt32()); + if (!sType.isEmpty() && !sIndex.isEmpty()) + { + sw::search::SearchIndexData aData; + aData.mnNodeIndex = sIndex.toInt32(); + aData.meType = sw::search::NodeType(sType.toInt32()); - aDataVector.push_back(aData); - } + rDataVector.push_back(aData); } - aWalker.next(); - } - aWalker.parent(); - - if (!aDataVector.empty()) - { - for (auto const& rSearchIndexData : aDataVector) - findOne(aResult, rSearchIndexData); } + aWalker.next(); } + aWalker.parent(); + return true; +} - return aResult; +LocationResult SearchResultLocator::findForPayload(const char* pPayload) +{ + std::vector<sw::search::SearchIndexData> aDataVector; + + // Try parse the payload as JSON, if not recognised as JSON, try parse + // it as XML + tryParseJSON(pPayload, aDataVector) || tryParseXML(pPayload, aDataVector); + + return find(aDataVector); } } // end sw namespace