[Libreoffice-commits] core.git: offapi/com offapi/UnoApi_offapi.mk sfx2/source sw/source unoxml/source

Noel Grandin (via logerrit) logerrit at kemper.freedesktop.org
Fri Jun 28 10:48:21 UTC 2019


 offapi/UnoApi_offapi.mk                    |    1 
 offapi/com/sun/star/rdf/XNamedGraph2.idl   |  134 ++++++++++++++
 sfx2/source/doc/DocumentMetadataAccess.cxx |   62 +-----
 sw/source/core/edit/edfcol.cxx             |    8 
 unoxml/source/rdf/librdf_repository.cxx    |  273 ++++++++++++++++++++++++++++-
 5 files changed, 424 insertions(+), 54 deletions(-)

New commits:
commit 47e04cf31c6165dd55dc20962ad9c72962b958bd
Author:     Noel Grandin <noel.grandin at collabora.co.uk>
AuthorDate: Wed Jun 26 15:19:36 2019 +0200
Commit:     Noel Grandin <noel.grandin at collabora.co.uk>
CommitDate: Fri Jun 28 12:47:35 2019 +0200

    tdf#125706 and tdf#125665 writer, speed up RDF
    
    The RDF stuff is sloooooooow, so (a) add some caching and (b) fold a
    very hot UNO_QUERY call.
    
    To add the caching we need to add a new UNO interface, since
    XEnumeration is not amenable to being cached.
    
    Add an optimised getStatementsGraph_NoLock2 that skips the intermediate
    enumeration object, and consequently a lot of locking/unlocking.
    
    Cache by OUString key, to avoid expensive OUString<->OString conversion
    when looking up entries in the cache.
    
    For the test document in tdf#125706, this takes the time from 7s to 5s for me.
    
    For the test document in tdf#125665, this takes the load time
    from 60s to 7s for me.
    
    Change-Id: I207387e975b4f107834edd0974134c481fb4012d
    Reviewed-on: https://gerrit.libreoffice.org/74740
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.grandin at collabora.co.uk>

diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index c86e7d5a7800..8a567218dec1 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -2981,6 +2981,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/rdf,\
 	XLiteral \
 	XMetadatable \
 	XNamedGraph \
+	XNamedGraph2 \
 	XNode \
 	XQuerySelectResult \
 	XReifiedStatement \
diff --git a/offapi/com/sun/star/rdf/XNamedGraph2.idl b/offapi/com/sun/star/rdf/XNamedGraph2.idl
new file mode 100644
index 000000000000..e90254a577d3
--- /dev/null
+++ b/offapi/com/sun/star/rdf/XNamedGraph2.idl
@@ -0,0 +1,134 @@
+/* -*- 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 .
+ */
+
+#ifndef __com_sun_star_rdf_XNamedGraph_idl__
+#define __com_sun_star_rdf_XNamedGraph_idl__
+
+#include <com/sun/star/rdf/XNamedGraph.idl>
+
+
+
+module com {   module sun {   module star {   module rdf {
+
+/** represents an RDF named graph that is stored in an RDF Repository.
+
+    @since LibreOffice 6.4
+ */
+interface XNamedGraph2 : XNamedGraph
+{
+
+    /** gets objects for matching RDF statements from a graph.
+
+        <p>this call caches its results (as opposed to getStatements in XNamedGraph
+        which cannot because it returns a mutable object).
+
+        <p>
+        Note that the ODF elements that can have metadata attached all
+        implement the interface XMetadatable, which inherits
+        from XResource, meaning that you can simply pass them
+        in as arguments here, and it will magically work.
+        </p>
+
+        <p>
+        Any parameter may be `NULL`, which acts as a wildcard.
+        For example, to get all statements about myURI:
+        <code>getStatements2(myURI, null, null)</code>
+        </p>
+
+        @param Subject
+            the subject of the RDF triple.
+
+        @param Predicate
+            the predicate of the RDF triple.
+
+        @param Object
+            the object of the RDF triple.
+
+        @returns
+            a sequence of XURIs for the objects in RDF statements
+            in the graph that match
+            the parameters, represented as an
+            enumeration of Statement
+
+        @throws com::sun::star::container::NoSuchElementException
+            if this graph does not exist in the repository any more
+
+        @throws RepositoryException
+            if an error occurs when accessing the repository.
+     */
+    sequence<com::sun::star::rdf::XURI> getStatementsObjects(
+            [in] XResource Subject,
+            [in] XURI Predicate,
+            [in] XNode Object)
+        raises( com::sun::star::container::NoSuchElementException,
+                RepositoryException );
+
+    /** returns true if there are matching RDF statements from a graph.
+
+        <p>this call caches its results (as opposed to getStatements in XNamedGraph
+        which cannot because it returns a mutable object).
+
+        <p>
+        Note that the ODF elements that can have metadata attached all
+        implement the interface XMetadatable, which inherits
+        from XResource, meaning that you can simply pass them
+        in as arguments here, and it will magically work.
+        </p>
+
+        <p>
+        Any parameter may be `NULL`, which acts as a wildcard.
+        For example, to get all statements about myURI:
+        <code>getStatements2(myURI, null, null)</code>
+        </p>
+
+        @param Subject
+            the subject of the RDF triple.
+
+        @param Predicate
+            the predicate of the RDF triple.
+
+        @param Object
+            the object of the RDF triple.
+
+        @returns
+            a sequence of XURIs for the objects in RDF statements
+            in the graph that match
+            the parameters, represented as an
+            enumeration of Statement
+
+        @throws com::sun::star::container::NoSuchElementException
+            if this graph does not exist in the repository any more
+
+        @throws RepositoryException
+            if an error occurs when accessing the repository.
+     */
+    boolean hasStatements(
+            [in] XResource Subject,
+            [in] XURI Predicate,
+            [in] XNode Object)
+        raises( com::sun::star::container::NoSuchElementException,
+                RepositoryException );
+};
+
+
+}; }; }; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/DocumentMetadataAccess.cxx b/sfx2/source/doc/DocumentMetadataAccess.cxx
index 14c5d98ca5d2..83cd4ec3f6d4 100644
--- a/sfx2/source/doc/DocumentMetadataAccess.cxx
+++ b/sfx2/source/doc/DocumentMetadataAccess.cxx
@@ -36,6 +36,7 @@
 #include <com/sun/star/rdf/Literal.hpp>
 #include <com/sun/star/rdf/URI.hpp>
 #include <com/sun/star/rdf/Repository.hpp>
+#include <com/sun/star/rdf/XNamedGraph2.hpp>
 
 #include <rtl/ustrbuf.hxx>
 #include <rtl/uri.hxx>
@@ -220,7 +221,7 @@ struct DocumentMetadataAccess_Impl
     const SfxObjectShell & m_rXmlIdRegistrySupplier;
     uno::Reference<rdf::XURI> m_xBaseURI;
     uno::Reference<rdf::XRepository> m_xRepository;
-    uno::Reference<rdf::XNamedGraph> m_xManifest;
+    uno::Reference<rdf::XNamedGraph2> m_xManifest;
     DocumentMetadataAccess_Impl(
             uno::Reference<uno::XComponentContext> const& i_xContext,
             SfxObjectShell const & i_rRegistrySupplier)
@@ -228,7 +229,6 @@ struct DocumentMetadataAccess_Impl
       , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
       , m_xBaseURI()
       , m_xRepository()
-      , m_xManifest()
     {
         OSL_ENSURE(m_xContext.is(), "context null");
     }
@@ -394,26 +394,12 @@ removeFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
     }
 }
 
-static ::std::vector< uno::Reference< rdf::XURI > >
+static css::uno::Sequence< uno::Reference< rdf::XURI > >
 getAllParts(struct DocumentMetadataAccess_Impl const & i_rImpl)
 {
-    ::std::vector< uno::Reference< rdf::XURI > > ret;
     try {
-        const uno::Reference<container::XEnumeration> xEnum(
-            i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI.get(),
-                getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), nullptr),
-            uno::UNO_SET_THROW);
-        while (xEnum->hasMoreElements()) {
-            rdf::Statement stmt;
-            if (!(xEnum->nextElement() >>= stmt)) {
-                throw uno::RuntimeException();
-            }
-            const uno::Reference<rdf::XURI> xPart(stmt.Object,
-                uno::UNO_QUERY);
-            if (!xPart.is()) continue;
-            ret.push_back(xPart);
-        }
-        return ret;
+        return i_rImpl.m_xManifest->getStatementsObjects( i_rImpl.m_xBaseURI.get(),
+                getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), nullptr);
     } catch (const uno::RuntimeException &) {
         throw;
     } catch (const uno::Exception &) {
@@ -454,27 +440,14 @@ getAllParts(struct DocumentMetadataAccess_Impl const& i_rImpl,
     ::std::vector<uno::Reference<rdf::XURI>> ret;
     try
     {
-        const uno::Reference<container::XEnumeration> xEnum(
-            i_rImpl.m_xManifest->getStatements(i_rImpl.m_xBaseURI.get(),
+        css::uno::Sequence< uno::Reference< rdf::XURI > > parts1
+            = i_rImpl.m_xManifest->getStatementsObjects(i_rImpl.m_xBaseURI.get(),
                                                getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
-                                               nullptr),
-            uno::UNO_SET_THROW);
-        while (xEnum->hasMoreElements())
+                                               nullptr);
+        for (auto const & xPart : parts1)
         {
-            rdf::Statement stmt;
-            if (!(xEnum->nextElement() >>= stmt))
-            {
-                throw uno::RuntimeException();
-            }
-            const uno::Reference<rdf::XURI> xPart(stmt.Object, uno::UNO_QUERY);
-            if (!xPart.is())
-                continue;
-
-            const uno::Reference<container::XEnumeration> xEnum2(
-                i_rImpl.m_xManifest->getStatements(
-                    xPart.get(), getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), i_xType.get()),
-                uno::UNO_SET_THROW);
-            if (xEnum2->hasMoreElements())
+            if (i_rImpl.m_xManifest->hasStatements(
+                    xPart.get(), getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), i_xType.get()))
                 ret.emplace_back(xPart);
         }
         return ret;
@@ -773,10 +746,11 @@ retry:
     }
 
     // init manifest graph
-    const uno::Reference<rdf::XNamedGraph> xManifestGraph(
-        i_rImpl.m_xRepository->getGraph(xManifest));
-    i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
-        i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
+    auto xManifestGraph = i_rImpl.m_xRepository->getGraph(xManifest);
+    if (xManifestGraph.is())
+        i_rImpl.m_xManifest.set(xManifestGraph, uno::UNO_QUERY_THROW);
+    else
+        i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_QUERY_THROW);
     const uno::Reference<container::XEnumeration> xEnum(
         i_rImpl.m_xManifest->getStatements(nullptr,
             getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
@@ -808,7 +782,7 @@ static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
 
         i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
             getURIForStream(i_rImpl, s_manifest)),
-            uno::UNO_SET_THROW);
+            uno::UNO_QUERY_THROW);
 
         // insert the document statement
         i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
@@ -1134,7 +1108,7 @@ void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
     std::vector< OUString > MfstMetadataFiles;
 
     try {
-        const ::std::vector< uno::Reference< rdf::XURI > > parts(
+        const css::uno::Sequence< uno::Reference< rdf::XURI > > parts(
             getAllParts(*m_pImpl) );
         const uno::Reference<rdf::XURI>& xContentFile(
             getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
diff --git a/sw/source/core/edit/edfcol.cxx b/sw/source/core/edit/edfcol.cxx
index 4df464ee49ed..a61964900a1d 100644
--- a/sw/source/core/edit/edfcol.cxx
+++ b/sw/source/core/edit/edfcol.cxx
@@ -1157,12 +1157,14 @@ void SwEditShell::SetClassification(const OUString& rName, SfxClassificationPoli
     }
 }
 
+// We pass xParent and xNodeSubject even though they point to the same thing because the UNO_QUERY is
+// on a performance-sensitive path.
 static void lcl_ApplyParagraphClassification(SwDoc* pDoc,
                                       const uno::Reference<frame::XModel>& xModel,
                                       const uno::Reference<text::XTextContent>& xParent,
+                                      const css::uno::Reference<css::rdf::XResource>& xNodeSubject,
                                       std::vector<svx::ClassificationResult> aResults)
 {
-    css::uno::Reference<css::rdf::XResource> xNodeSubject(xParent, uno::UNO_QUERY);
     if (!xNodeSubject.is())
         return;
 
@@ -1279,7 +1281,7 @@ void SwEditShell::ApplyParagraphClassification(std::vector<svx::ClassificationRe
 
     uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
     uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
-    lcl_ApplyParagraphClassification(GetDoc(), xModel, xParent, std::move(aResults));
+    lcl_ApplyParagraphClassification(GetDoc(), xModel, xParent, css::uno::Reference<css::rdf::XResource>(xParent, uno::UNO_QUERY), std::move(aResults));
 }
 
 static std::vector<svx::ClassificationResult> lcl_CollectParagraphClassification(const uno::Reference<frame::XModel>& xModel, const uno::Reference<text::XTextContent>& xParagraph)
@@ -1987,7 +1989,7 @@ void SwEditShell::RestoreMetadataFieldsAndValidateParagraphSignatures()
             }
 
             // Update classification based on results.
-            lcl_ApplyParagraphClassification(GetDoc(), xModel, xParagraph, aResults);
+            lcl_ApplyParagraphClassification(GetDoc(), xModel, xParagraph, xSubject, aResults);
 
             // Get Signatures
             std::map<OUString, SignatureDescr> aSignatures;
diff --git a/unoxml/source/rdf/librdf_repository.cxx b/unoxml/source/rdf/librdf_repository.cxx
index c04e4a0155ce..3b774b7526c9 100644
--- a/unoxml/source/rdf/librdf_repository.cxx
+++ b/unoxml/source/rdf/librdf_repository.cxx
@@ -51,9 +51,11 @@
 #include <com/sun/star/rdf/BlankNode.hpp>
 #include <com/sun/star/rdf/URI.hpp>
 #include <com/sun/star/rdf/Literal.hpp>
+#include <com/sun/star/rdf/XNamedGraph2.hpp>
 
 #include <rtl/ref.hxx>
 #include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
 #include <rtl/ustring.hxx>
 #include <osl/diagnose.h>
 #include <cppuhelper/exc_hlp.hxx>
@@ -183,23 +185,23 @@ public:
     struct Resource : public Node { };
     struct URI : public Resource
     {
-        OString const value;
+        OString value;
         explicit URI(OString const& i_rValue)
             : value(i_rValue)
         { }
     };
     struct BlankNode : public Resource
     {
-        OString const value;
+        OString value;
         explicit BlankNode(OString const& i_rValue)
             : value(i_rValue)
         { }
     };
     struct Literal : public Node
     {
-        OString const value;
-        OString const language;
-        ::boost::optional<OString> const type;
+        OString value;
+        OString language;
+        ::boost::optional<OString> type;
         Literal(OString const& i_rValue, OString const& i_rLanguage,
                 ::boost::optional<OString> const& i_rType)
             : value(i_rValue)
@@ -242,8 +244,14 @@ public:
         Statement const& i_rStatement);
     static std::shared_ptr<Resource> extractResource_NoLock(
         const uno::Reference< rdf::XResource > & i_xResource);
+    static void extractResourceToCacheKey(
+        const uno::Reference< rdf::XResource > & i_xResource,
+        OUStringBuffer& rBuf);
     static std::shared_ptr<Node> extractNode_NoLock(
         const uno::Reference< rdf::XNode > & i_xNode);
+    static void extractNodeToCacheKey(
+        const uno::Reference< rdf::XNode > & i_xNode,
+        OUStringBuffer& rBuffer);
     static Statement extractStatement_NoLock(
         const uno::Reference< rdf::XResource > & i_xSubject,
         const uno::Reference< rdf::XURI > & i_xPredicate,
@@ -369,6 +377,14 @@ public:
 //        throw (uno::RuntimeException, lang::IllegalArgumentException,
 //            container::NoSuchElementException, rdf::RepositoryException);
 
+    // Optimised version that skips the enumeration object
+    std::vector<uno::Reference<rdf::XURI>> getStatementsGraph_NoLock2(
+            const uno::Reference< rdf::XResource > & i_xSubject,
+            const uno::Reference< rdf::XURI > & i_xPredicate,
+            const uno::Reference< rdf::XNode > & i_xObject,
+            const uno::Reference< rdf::XURI > & i_xName,
+            bool i_Internal = false );
+
     const librdf_TypeConverter& getTypeConverter() { return m_TypeConverter; };
 
 private:
@@ -646,7 +662,7 @@ librdf_QuerySelectResult::getBindingNames()
  */
 class librdf_NamedGraph:
     public ::cppu::WeakImplHelper<
-        rdf::XNamedGraph>
+        rdf::XNamedGraph2>
 {
 public:
     librdf_NamedGraph(librdf_Repository * i_pRep,
@@ -678,16 +694,38 @@ public:
             const uno::Reference< rdf::XResource > & i_xSubject,
             const uno::Reference< rdf::XURI > & i_xPredicate,
             const uno::Reference< rdf::XNode > & i_xObject) override;
+    // css::rdf::XNamedGraph2
+    virtual uno::Sequence<uno::Reference< rdf::XURI >> SAL_CALL getStatementsObjects(
+            const uno::Reference< rdf::XResource > & i_xSubject,
+            const uno::Reference< rdf::XURI > & i_xPredicate,
+            const uno::Reference< rdf::XNode > & i_xObject) override;
+    virtual sal_Bool SAL_CALL hasStatements(
+            const uno::Reference< rdf::XResource > & i_xSubject,
+            const uno::Reference< rdf::XURI > & i_xPredicate,
+            const uno::Reference< rdf::XNode > & i_xObject) override;
 
 private:
 
     librdf_NamedGraph(librdf_NamedGraph const&) = delete;
     librdf_NamedGraph& operator=(librdf_NamedGraph const&) = delete;
 
+    static OUString createCacheKey(
+        const uno::Reference< rdf::XResource > & i_xSubject,
+        const uno::Reference< rdf::XURI > & i_xPredicate,
+        const uno::Reference< rdf::XNode > & i_xObject);
+
     /// weak reference: this is needed to check if m_pRep is valid
     uno::WeakReference< rdf::XRepository > const m_wRep;
     librdf_Repository *const m_pRep;
     uno::Reference< rdf::XURI > const m_xName;
+
+    // Querying is rather slow, so cache the results.
+    struct CacheValue
+    {
+        std::vector<uno::Reference<rdf::XURI>> maUris;
+        bool mbHasNodes;
+    };
+    std::map<OUString, CacheValue> m_aStatementsCache;
 };
 
 
@@ -729,6 +767,7 @@ void SAL_CALL librdf_NamedGraph::clear()
         throw lang::WrappedTargetRuntimeException( ex.Message,
                         *this, anyEx );
     }
+    m_aStatementsCache.clear();
 }
 
 void SAL_CALL librdf_NamedGraph::addStatement(
@@ -743,6 +782,7 @@ void SAL_CALL librdf_NamedGraph::addStatement(
     }
     m_pRep->addStatementGraph_NoLock(
             i_xSubject, i_xPredicate, i_xObject, m_xName);
+    m_aStatementsCache.clear();
 }
 
 void SAL_CALL librdf_NamedGraph::removeStatements(
@@ -757,6 +797,7 @@ void SAL_CALL librdf_NamedGraph::removeStatements(
     }
     m_pRep->removeStatementsGraph_NoLock(
             i_xSubject, i_xPredicate, i_xObject, m_xName);
+    m_aStatementsCache.clear();
 }
 
 uno::Reference< container::XEnumeration > SAL_CALL
@@ -770,10 +811,86 @@ librdf_NamedGraph::getStatements(
         throw rdf::RepositoryException(
             "librdf_NamedGraph::getStatements: repository is gone", *this);
     }
-    return m_pRep->getStatementsGraph_NoLock(
+    auto ret = m_pRep->getStatementsGraph_NoLock(
+            i_xSubject, i_xPredicate, i_xObject, m_xName);
+    return ret;
+}
+
+OUString librdf_NamedGraph::createCacheKey(
+    const uno::Reference< rdf::XResource > & i_xSubject,
+    const uno::Reference< rdf::XURI > & i_xPredicate,
+    const uno::Reference< rdf::XNode > & i_xObject)
+{
+    OUStringBuffer cacheKey(256);
+    librdf_TypeConverter::extractResourceToCacheKey(i_xSubject, cacheKey);
+    cacheKey.append("\t");
+    librdf_TypeConverter::extractResourceToCacheKey(i_xPredicate, cacheKey);
+    cacheKey.append("\t");
+    librdf_TypeConverter::extractNodeToCacheKey(i_xObject, cacheKey);
+    return cacheKey.makeStringAndClear();
+}
+
+uno::Sequence<uno::Reference<rdf::XURI>> SAL_CALL
+librdf_NamedGraph::getStatementsObjects(
+    const uno::Reference< rdf::XResource > & i_xSubject,
+    const uno::Reference< rdf::XURI > & i_xPredicate,
+    const uno::Reference< rdf::XNode > & i_xObject)
+{
+    OUString cacheKey = createCacheKey(i_xSubject, i_xPredicate, i_xObject);
+    auto it = m_aStatementsCache.find(cacheKey);
+    if (it != m_aStatementsCache.end())
+        return comphelper::containerToSequence(it->second.maUris);
+
+    uno::Reference< rdf::XRepository > xRep( m_wRep );
+    if (!xRep.is()) {
+        throw rdf::RepositoryException(
+            "librdf_NamedGraph::getStatements: repository is gone", *this);
+    }
+    std::vector<uno::Reference<rdf::XURI>> xEnum = m_pRep->getStatementsGraph_NoLock2(
             i_xSubject, i_xPredicate, i_xObject, m_xName);
+
+    std::vector<uno::Reference<rdf::XURI>> uris;
+    bool bHasNodes = false;
+    for (uno::Reference<rdf::XURI> const & rPart : xEnum)
+    {
+        bHasNodes = true;
+        if (!rPart.is())
+            continue;
+        uris.push_back(rPart);
+    }
+    m_aStatementsCache.emplace(cacheKey, CacheValue{ uris, bHasNodes });
+    return comphelper::containerToSequence(uris);
 }
 
+sal_Bool SAL_CALL
+librdf_NamedGraph::hasStatements(
+    const uno::Reference< rdf::XResource > & i_xSubject,
+    const uno::Reference< rdf::XURI > & i_xPredicate,
+    const uno::Reference< rdf::XNode > & i_xObject)
+{
+    OUString cacheKey = createCacheKey(i_xSubject, i_xPredicate, i_xObject);
+    auto it = m_aStatementsCache.find(cacheKey);
+    if (it != m_aStatementsCache.end())
+        return it->second.mbHasNodes;
+    uno::Reference< rdf::XRepository > xRep( m_wRep );
+    if (!xRep.is()) {
+        throw rdf::RepositoryException(
+            "librdf_NamedGraph::getStatements: repository is gone", *this);
+    }
+    std::vector<uno::Reference<rdf::XURI>> xEnum = m_pRep->getStatementsGraph_NoLock2(
+            i_xSubject, i_xPredicate, i_xObject, m_xName);
+    std::vector<uno::Reference<rdf::XURI>> uris;
+    bool bHasNodes = false;
+    for (uno::Reference<rdf::XURI> const & rPart : xEnum)
+    {
+        bHasNodes = true;
+        if (!rPart.is())
+            continue;
+        uris.push_back(rPart);
+    }
+    m_aStatementsCache.emplace(cacheKey, CacheValue{ uris, bHasNodes });
+    return bHasNodes;
+}
 
 std::shared_ptr<librdf_world> librdf_Repository::m_pWorld;
 sal_uInt32 librdf_Repository::m_NumInstances = 0;
@@ -1921,6 +2038,108 @@ librdf_Repository::getStatementsGraph_NoLock(
     return new librdf_GraphResult(this, m_aMutex, pStream, pContext);
 }
 
+// Optimised version that skips the enumeration object
+std::vector<uno::Reference<rdf::XURI>>
+librdf_Repository::getStatementsGraph_NoLock2(
+    const uno::Reference< rdf::XResource > & i_xSubject,
+    const uno::Reference< rdf::XURI > & i_xPredicate,
+    const uno::Reference< rdf::XNode > & i_xObject,
+    const uno::Reference< rdf::XURI > & i_xGraphName,
+    bool i_Internal)
+//throw (uno::RuntimeException, lang::IllegalArgumentException,
+//    container::NoSuchElementException, rdf::RepositoryException)
+{
+    std::vector<uno::Reference<rdf::XURI>> ret;
+
+    // N.B.: if any of subject, predicate, object is an XMetadatable, and
+    // has no metadata reference, then there cannot be any node in the graph
+    // representing it; in order to prevent side effect
+    // (ensureMetadataReference), check for this condition and return
+    if (isMetadatableWithoutMetadata(i_xSubject)   ||
+        isMetadatableWithoutMetadata(i_xPredicate) ||
+        isMetadatableWithoutMetadata(i_xObject))
+    {
+        return ret;
+    }
+
+    librdf_TypeConverter::Statement const stmt(
+        librdf_TypeConverter::extractStatement_NoLock(
+            i_xSubject, i_xPredicate, i_xObject));
+    const OUString contextU( i_xGraphName->getStringValue() );
+
+    ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
+
+    if (!i_Internal && (m_NamedGraphs.find(contextU) == m_NamedGraphs.end())) {
+        throw container::NoSuchElementException(
+                "librdf_Repository::getStatements: "
+                "no graph with given URI exists", *this);
+    }
+    const OString context(
+        OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) );
+
+    const std::shared_ptr<librdf_node> pContext(
+        librdf_new_node_from_uri_string(m_pWorld.get(),
+            reinterpret_cast<const unsigned char*> (context.getStr())),
+        safe_librdf_free_node);
+    if (!pContext) {
+        throw uno::RuntimeException(
+            "librdf_Repository::getStatements: "
+            "librdf_new_node_from_uri_string failed", *this);
+    }
+    const std::shared_ptr<librdf_statement> pStatement(
+        librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt),
+        safe_librdf_free_statement);
+    OSL_ENSURE(pStatement, "mkStatement failed");
+
+    const std::shared_ptr<librdf_stream> pStream(
+        librdf_model_find_statements_in_context(m_pModel.get(),
+            pStatement.get(), pContext.get()),
+        safe_librdf_free_stream);
+    if (!pStream) {
+        throw rdf::RepositoryException(
+            "librdf_Repository::getStatements: "
+            "librdf_model_find_statements_in_context failed", *this);
+    }
+
+    librdf_node *pCtxt1(
+#if LIBRDF_VERSION >= 10012
+        librdf_stream_get_context2(pStream.get()) );
+#else
+        static_cast<librdf_node *>(librdf_stream_get_context(pStream.get())) );
+#endif
+    while (!librdf_stream_end(pStream.get()))
+    {
+        auto pCtxt = pCtxt1;
+        librdf_statement *pStmt( librdf_stream_get_object(pStream.get()) );
+        if (!pStmt) {
+            rdf::QueryException e(
+                "librdf_GraphResult::nextElement: "
+                "librdf_stream_get_object failed", *this);
+            throw lang::WrappedTargetException(
+                "librdf_GraphResult::nextElement: "
+                "librdf_stream_get_object failed", *this,
+                    uno::makeAny(e));
+        }
+        // NB: pCtxt may be null here if this is result of a graph query
+        if (pCtxt && isInternalContext(pCtxt)) {
+            pCtxt = nullptr; // XML ID context is implementation detail!
+        }
+
+        ret.push_back(
+            css::uno::Reference<rdf::XURI>(
+                getTypeConverter().convertToXNode(librdf_statement_get_object(pStmt)),
+                uno::UNO_QUERY) );
+
+        // NB: this will invalidate current item.
+        librdf_stream_next(pStream.get());
+    }
+
+    return ret;
+    // librdf_model_find_statements_in_context is buggy and does not put
+    // the context into result statements; pass it to librdf_GraphResult here
+    //return new librdf_GraphResult(this, m_aMutex, pStream, pContext);
+}
+
 extern "C"
 void librdf_raptor_init(void* /*user_data*/, raptor_world* pRaptorWorld)
 {
@@ -2031,6 +2250,21 @@ librdf_TypeConverter::extractResource_NoLock(
     }
 }
 
+void
+librdf_TypeConverter::extractResourceToCacheKey(
+    const uno::Reference< rdf::XResource > & i_xResource, OUStringBuffer& rBuffer)
+{
+    if (!i_xResource.is()) {
+        return;
+    }
+    uno::Reference< rdf::XBlankNode > xBlankNode(i_xResource, uno::UNO_QUERY);
+    if (xBlankNode.is()) {
+        rBuffer.append("BlankNode ").append(xBlankNode->getStringValue());
+    } else { // assumption: everything else is URI
+        rBuffer.append("URI ").append(i_xResource->getStringValue());
+    }
+}
+
 // create blank or URI node
 librdf_node* librdf_TypeConverter::mkResource_Lock( librdf_world* i_pWorld,
     Resource const*const i_pResource)
@@ -2098,6 +2332,31 @@ librdf_TypeConverter::extractNode_NoLock(
     return std::shared_ptr<Node>(new Literal(val, lang, type));
 }
 
+// extract blank or URI or literal node - call without Mutex locked
+void
+librdf_TypeConverter::extractNodeToCacheKey(
+    const uno::Reference< rdf::XNode > & i_xNode,
+    OUStringBuffer& rBuffer)
+{
+    if (!i_xNode.is()) {
+        return;
+    }
+    uno::Reference< rdf::XResource > xResource(i_xNode, uno::UNO_QUERY);
+    if (xResource.is()) {
+        return extractResourceToCacheKey(xResource, rBuffer);
+    }
+    uno::Reference< rdf::XLiteral> xLiteral(i_xNode, uno::UNO_QUERY);
+    OSL_ENSURE(xLiteral.is(),
+        "mkNode: someone invented a new rdf.XNode and did not tell me");
+    if (!xLiteral.is()) {
+        return;
+    }
+    rBuffer.append("Literal ").append(xLiteral->getValue()).append("\t").append(xLiteral->getLanguage());
+    const uno::Reference< rdf::XURI > xType(xLiteral->getDatatype());
+    if (xType.is())
+        rBuffer.append(xType->getStringValue());
+}
+
 // create blank or URI or literal node
 librdf_node* librdf_TypeConverter::mkNode_Lock( librdf_world* i_pWorld,
     Node const*const i_pNode)


More information about the Libreoffice-commits mailing list