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

Reply via email to