/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** Implementation of the service com.sun.star.rdf.Repository. This implementation uses the Redland RDF library (librdf). There are several classes involved: librdf_TypeConverter: helper class to convert data types redland <-> uno librdf_Repository: the main repository, does almost all the work librdf_NamedGraph: the XNamedGraph, forwards everything to repository librdf_GraphResult: an XEnumeration librdf_QuerySelectResult: an XEnumeration> */ /// anonymous implementation namespace namespace { class librdf_NamedGraph; class librdf_Repository; using namespace ::com::sun::star; typedef std::map< OUString, ::rtl::Reference > NamedGraphMap_t; const char s_sparql [] = "sparql"; const char s_nsOOo [] = "http://openoffice.org/2004/office/rdfa/"; //FIXME: this approach is not ideal. can we use blank nodes instead? bool isInternalContext(librdf_node *i_pNode) noexcept { OSL_ENSURE(i_pNode, "isInternalContext: context null"); OSL_ENSURE(librdf_node_is_resource(i_pNode), "isInternalContext: context not resource"); if (i_pNode) { librdf_uri *pURI(librdf_node_get_uri(i_pNode)); OSL_ENSURE(pURI, "isInternalContext: URI null"); if (pURI) { unsigned char *pContextURI(librdf_uri_as_string(pURI)); assert(pContextURI && "isInternalContext: URI string null"); // if prefix matches reserved uri, it is RDFa context if (!strncmp(reinterpret_cast(pContextURI), s_nsOOo, sizeof(s_nsOOo)-1)) { return true; } } return false; } return true; } // n.b.: librdf destructor functions dereference null pointers! // so they need to be wrapped to be usable with std::shared_ptr. void safe_librdf_free_world(librdf_world *const world) { if (world) { librdf_free_world(world); } } void safe_librdf_free_model(librdf_model *const model) { if (model) { librdf_free_model(model); } } void safe_librdf_free_node(librdf_node* node) { if (node) { librdf_free_node(node); } } void safe_librdf_free_parser(librdf_parser *const parser) { if (parser) { librdf_free_parser(parser); } } void safe_librdf_free_query(librdf_query *const query) { if (query) { librdf_free_query(query); } } void safe_librdf_free_query_results(librdf_query_results *const query_results) { if (query_results) { librdf_free_query_results(query_results); } } void safe_librdf_free_serializer(librdf_serializer *const serializer) { if (serializer) { librdf_free_serializer(serializer); } } void safe_librdf_free_statement(librdf_statement *const statement) { if (statement) { librdf_free_statement(statement); } } void safe_librdf_free_storage(librdf_storage *const storage) { if (storage) { librdf_free_storage(storage); } } void safe_librdf_free_stream(librdf_stream *const stream) { if (stream) { librdf_free_stream(stream); } } void safe_librdf_free_uri(librdf_uri *const uri) { if (uri) { librdf_free_uri(uri); } } /** converts between librdf types and UNO API types. */ class librdf_TypeConverter { public: // some wrapper classes to temporarily hold values of UNO XNodes struct Node { virtual ~Node() {} }; struct Resource : public Node { }; struct URI : public Resource { OString const value; explicit URI(OString const& i_rValue) : value(i_rValue) { } }; struct BlankNode : public Resource { OString const value; explicit BlankNode(OString const& i_rValue) : value(i_rValue) { } }; struct Literal : public Node { OString const value; OString const language; ::std::optional const type; Literal(OString const& i_rValue, OString const& i_rLanguage, ::std::optional const& i_rType) : value(i_rValue) , language(i_rLanguage) , type(i_rType) { } }; struct Statement { std::shared_ptr const pSubject; std::shared_ptr const pPredicate; std::shared_ptr const pObject; Statement(std::shared_ptr const& i_pSubject, std::shared_ptr const& i_pPredicate, std::shared_ptr const& i_pObject) : pSubject(i_pSubject) , pPredicate(i_pPredicate) , pObject(i_pObject) { } }; librdf_TypeConverter( uno::Reference< uno::XComponentContext > const & i_xContext, librdf_Repository &i_rRep) : m_xContext(i_xContext) , m_rRep(i_rRep) { }; librdf_world *createWorld_Lock() const; librdf_storage *createStorage_Lock(librdf_world *i_pWorld) const; librdf_model *createModel_Lock(librdf_world *i_pWorld, librdf_storage * i_pStorage) const; static librdf_uri* mkURI_Lock(librdf_world* i_pWorld, const OString & i_rURI); static librdf_node* mkResource_Lock(librdf_world* i_pWorld, const Resource * i_pResource); static librdf_node* mkNode_Lock(librdf_world* i_pWorld, const Node * i_pNode); static librdf_statement* mkStatement_Lock(librdf_world* i_pWorld, Statement const& i_rStatement); static std::shared_ptr extractResource_NoLock( const uno::Reference< rdf::XResource > & i_xResource); static void extractResourceToCacheKey_NoLock( const uno::Reference< rdf::XResource > & i_xResource, OUStringBuffer& rBuf); static std::shared_ptr extractNode_NoLock( const uno::Reference< rdf::XNode > & i_xNode); static void extractNodeToCacheKey_NoLock( 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, const uno::Reference< rdf::XNode > & i_xObject); uno::Reference convertToXURI(librdf_uri* i_pURI) const; uno::Reference convertToXURI(librdf_node* i_pURI) const; uno::Reference convertToXResource(librdf_node* i_pNode) const; uno::Reference convertToXNode(librdf_node* i_pNode) const; rdf::Statement convertToStatement(librdf_statement* i_pStmt, librdf_node* i_pContext) const; private: uno::Reference< uno::XComponentContext > const m_xContext; librdf_Repository & m_rRep; }; /** implements the repository service. */ class librdf_Repository: // private ::cppu::BaseMutex, public ::cppu::WeakImplHelper< lang::XServiceInfo, rdf::XDocumentRepository, lang::XInitialization> { public: explicit librdf_Repository( uno::Reference< uno::XComponentContext > const & i_xContext); virtual ~librdf_Repository() override; // css::lang::XServiceInfo: virtual OUString SAL_CALL getImplementationName() override; virtual sal_Bool SAL_CALL supportsService( const OUString & ServiceName) override; virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; // css::rdf::XRepository: virtual uno::Reference< rdf::XBlankNode > SAL_CALL createBlankNode() override; virtual uno::Reference SAL_CALL importGraph( ::sal_Int16 i_Format, const uno::Reference< io::XInputStream > & i_xInStream, const uno::Reference< rdf::XURI > & i_xGraphName, const uno::Reference< rdf::XURI > & i_xBaseURI) override; virtual void SAL_CALL exportGraph(::sal_Int16 i_Format, const uno::Reference< io::XOutputStream > & i_xOutStream, const uno::Reference< rdf::XURI > & i_xGraphName, const uno::Reference< rdf::XURI > & i_xBaseURI) override; virtual uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL getGraphNames() override; virtual uno::Reference< rdf::XNamedGraph > SAL_CALL getGraph( const uno::Reference< rdf::XURI > & i_xGraphName) override; virtual uno::Reference< rdf::XNamedGraph > SAL_CALL createGraph( const uno::Reference< rdf::XURI > & i_xGraphName) override; virtual void SAL_CALL destroyGraph( const uno::Reference< rdf::XURI > & i_xGraphName) override; virtual uno::Reference< container::XEnumeration > SAL_CALL getStatements( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) override; virtual uno::Reference< rdf::XQuerySelectResult > SAL_CALL querySelect(const OUString & i_rQuery) override; virtual uno::Reference< container::XEnumeration > SAL_CALL queryConstruct(const OUString & i_rQuery) override; virtual sal_Bool SAL_CALL queryAsk(const OUString & i_rQuery) override; // css::rdf::XDocumentRepository: virtual void SAL_CALL setStatementRDFa( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Sequence< uno::Reference< rdf::XURI > > & i_rPredicates, const uno::Reference< rdf::XMetadatable > & i_xObject, const OUString & i_rRDFaContent, const uno::Reference< rdf::XURI > & i_xRDFaDatatype) override; virtual void SAL_CALL removeStatementRDFa( const uno::Reference< rdf::XMetadatable > & i_xElement) override; virtual beans::Pair< uno::Sequence, sal_Bool > SAL_CALL getStatementRDFa(uno::Reference< rdf::XMetadatable > const& i_xElement) override; virtual uno::Reference< container::XEnumeration > SAL_CALL getStatementsRDFa( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) override; // css::lang::XInitialization: virtual void SAL_CALL initialize( const uno::Sequence< css::uno::Any > & i_rArguments) override; // XNamedGraph forwards --------------------------------------------- NamedGraphMap_t::iterator clearGraph_NoLock( const OUString & i_rGraphName, bool i_Internal = false ); NamedGraphMap_t::iterator clearGraph_Lock( const OUString & i_rGraphName, bool i_Internal); void addStatementGraph_NoLock( 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 ); // throw (uno::RuntimeException, lang::IllegalArgumentException, // container::NoSuchElementException, rdf::RepositoryException); void addStatementGraph_Lock( librdf_TypeConverter::Statement const& i_rStatement, OUString const& i_rGraphName, bool i_Internal); void removeStatementsGraph_NoLock( 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 ); // throw (uno::RuntimeException, lang::IllegalArgumentException, // container::NoSuchElementException, rdf::RepositoryException); std::vector getStatementsGraph_NoLock( 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 ); // throw (uno::RuntimeException, lang::IllegalArgumentException, // container::NoSuchElementException, rdf::RepositoryException); const librdf_TypeConverter& getTypeConverter() const { return m_TypeConverter; }; private: librdf_Repository(librdf_Repository const&) = delete; librdf_Repository& operator=(librdf_Repository const&) = delete; /// this is const, no need to lock m_aMutex to access it uno::Reference< uno::XComponentContext > const m_xContext; /// librdf global data /** N.B.: The redland documentation gives the impression that you can have as many librdf_worlds as you like. This is true in the same sense that you can physically be in as many places as you like. Well, you can, just not at the same time. The ugly truth is that destroying a librdf_world kills a bunch of static variables; other librdf_worlds become very unhappy when they access these. And of course this is not documented anywhere that I could find. So we allocate a single world, and refcount that. */ static std::shared_ptr m_pWorld; /// refcount static sal_uInt32 m_NumInstances; /// mutex for m_pWorld - redland is not as threadsafe as is often claimed static std::mutex m_aMutex; // NB: sequence of the shared pointers is important! /// librdf repository storage std::shared_ptr m_pStorage; /// librdf repository model std::shared_ptr m_pModel; /// all named graphs NamedGraphMap_t m_NamedGraphs; /// type conversion helper - stateless librdf_TypeConverter m_TypeConverter; /// set of xml:ids of elements with xhtml:content ::std::set< OUString > m_RDFaXHTMLContentSet; }; /** result of operations that return a graph, i.e., an XEnumeration of statements. */ class librdf_GraphResult: public ::cppu::WeakImplHelper< container::XEnumeration> { public: librdf_GraphResult(librdf_Repository *i_pRepository, std::mutex & i_rMutex, std::shared_ptr const& i_pStream, std::shared_ptr const& i_pContext, std::shared_ptr const& i_pQuery = std::shared_ptr() ) : m_xRep(i_pRepository) , m_rMutex(i_rMutex) , m_pQuery(i_pQuery) , m_pContext(i_pContext) , m_pStream(i_pStream) { }; virtual ~librdf_GraphResult() override { std::scoped_lock g(m_rMutex); // lock mutex when destroying members const_cast& >(m_pStream).reset(); const_cast& >(m_pContext).reset(); const_cast& >(m_pQuery).reset(); } // css::container::XEnumeration: virtual sal_Bool SAL_CALL hasMoreElements() override; virtual uno::Any SAL_CALL nextElement() override; private: librdf_GraphResult(librdf_GraphResult const&) = delete; librdf_GraphResult& operator=(librdf_GraphResult const&) = delete; // NB: this is not a weak pointer: streams _must_ be deleted before the // storage they point into, so we keep the repository alive here // also, sequence is important: the stream must be destroyed first. ::rtl::Reference< librdf_Repository > m_xRep; // needed for synchronizing access to librdf (it doesn't do win32 threading) std::mutex & m_rMutex; // the query (in case this is a result of a graph query) // not that the redland documentation spells this out explicitly, but // queries must be freed only after all the results are completely read std::shared_ptr const m_pQuery; std::shared_ptr const m_pContext; std::shared_ptr const m_pStream; librdf_node* getContext_Lock() const; }; // css::container::XEnumeration: sal_Bool SAL_CALL librdf_GraphResult::hasMoreElements() { std::scoped_lock g(m_rMutex); return m_pStream && !librdf_stream_end(m_pStream.get()); } librdf_node* librdf_GraphResult::getContext_Lock() const { if (!m_pStream || librdf_stream_end(m_pStream.get())) return nullptr; librdf_node *pCtxt( #if LIBRDF_VERSION >= 10012 librdf_stream_get_context2(m_pStream.get()) ); #else static_cast(librdf_stream_get_context(m_pStream.get())) ); #endif if (pCtxt) return pCtxt; return m_pContext.get(); } css::uno::Any SAL_CALL librdf_GraphResult::nextElement() { std::scoped_lock g(m_rMutex); if (m_pStream && librdf_stream_end(m_pStream.get())) { throw container::NoSuchElementException(); } librdf_node * pCtxt = getContext_Lock(); librdf_statement *pStmt( librdf_stream_get_object(m_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::Any(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! } rdf::Statement Stmt( m_xRep->getTypeConverter().convertToStatement(pStmt, pCtxt) ); // NB: this will invalidate current item. librdf_stream_next(m_pStream.get()); return uno::Any(Stmt); } /** result of operations that return a graph, i.e., an XEnumeration of statements. */ class librdf_GraphResult2: public ::cppu::WeakImplHelper< container::XEnumeration> { public: librdf_GraphResult2(std::vector statements) : m_vStatements(std::move(statements)) { }; // css::container::XEnumeration: virtual sal_Bool SAL_CALL hasMoreElements() override; virtual uno::Any SAL_CALL nextElement() override; private: std::vector m_vStatements; std::atomic m_nIndex = 0; }; // css::container::XEnumeration: sal_Bool SAL_CALL librdf_GraphResult2::hasMoreElements() { return m_nIndex < m_vStatements.size(); } css::uno::Any SAL_CALL librdf_GraphResult2::nextElement() { std::size_t const n = m_nIndex++; if (m_vStatements.size() <= n) { m_nIndex = m_vStatements.size(); // avoid overflow throw container::NoSuchElementException(); } return uno::Any(m_vStatements[n]); } /** result of tuple queries ("SELECT"). */ class librdf_QuerySelectResult: public ::cppu::WeakImplHelper< rdf::XQuerySelectResult> { public: librdf_QuerySelectResult(librdf_Repository *i_pRepository, std::mutex & i_rMutex, std::shared_ptr const& i_pQuery, std::shared_ptr const& i_pQueryResult, uno::Sequence< OUString > const& i_rBindingNames ) : m_xRep(i_pRepository) , m_rMutex(i_rMutex) , m_pQuery(i_pQuery) , m_pQueryResult(i_pQueryResult) , m_BindingNames(i_rBindingNames) { }; virtual ~librdf_QuerySelectResult() override { std::scoped_lock g(m_rMutex); // lock mutex when destroying members const_cast& >(m_pQueryResult) .reset(); const_cast& >(m_pQuery).reset(); } // css::container::XEnumeration: virtual sal_Bool SAL_CALL hasMoreElements() override; virtual uno::Any SAL_CALL nextElement() override; // css::rdf::XQuerySelectResult: virtual uno::Sequence< OUString > SAL_CALL getBindingNames() override; private: librdf_QuerySelectResult(librdf_QuerySelectResult const&) = delete; librdf_QuerySelectResult& operator=(librdf_QuerySelectResult const&) = delete; // NB: this is not a weak pointer: streams _must_ be deleted before the // storage they point into, so we keep the repository alive here // also, sequence is important: the stream must be destroyed first. ::rtl::Reference< librdf_Repository > m_xRep; // needed for synchronizing access to librdf (it doesn't do win32 threading) std::mutex & m_rMutex; // not that the redland documentation spells this out explicitly, but // queries must be freed only after all the results are completely read std::shared_ptr const m_pQuery; std::shared_ptr const m_pQueryResult; uno::Sequence< OUString > const m_BindingNames; }; // css::container::XEnumeration: sal_Bool SAL_CALL librdf_QuerySelectResult::hasMoreElements() { std::scoped_lock g(m_rMutex); return !librdf_query_results_finished(m_pQueryResult.get()); } class NodeArray : private std::vector { public: NodeArray(int cnt) : std::vector(cnt) {} ~NodeArray() noexcept { std::for_each(begin(), end(), safe_librdf_free_node); } using std::vector::data; using std::vector::operator[]; }; css::uno::Any SAL_CALL librdf_QuerySelectResult::nextElement() { std::scoped_lock g(m_rMutex); if (librdf_query_results_finished(m_pQueryResult.get())) { throw container::NoSuchElementException(); } sal_Int32 count(m_BindingNames.getLength()); OSL_ENSURE(count >= 0, "negative length?"); NodeArray aNodes(count); if (librdf_query_results_get_bindings(m_pQueryResult.get(), nullptr, aNodes.data())) { rdf::QueryException e( "librdf_QuerySelectResult::nextElement: " "librdf_query_results_get_bindings failed", *this); throw lang::WrappedTargetException( "librdf_QuerySelectResult::nextElement: " "librdf_query_results_get_bindings failed", *this, uno::Any(e)); } uno::Sequence< uno::Reference< rdf::XNode > > ret(count); auto retRange = asNonConstRange(ret); for (int i = 0; i < count; ++i) { retRange[i] = m_xRep->getTypeConverter().convertToXNode(aNodes[i]); } // NB: this will invalidate current item. librdf_query_results_next(m_pQueryResult.get()); return uno::Any(ret); } // css::rdf::XQuerySelectResult: uno::Sequence< OUString > SAL_CALL librdf_QuerySelectResult::getBindingNames() { // const - no lock needed return m_BindingNames; } /** represents a named graph, and forwards all the work to repository. */ class librdf_NamedGraph: public ::cppu::WeakImplHelper< rdf::XNamedGraph> { public: librdf_NamedGraph(librdf_Repository * i_pRep, uno::Reference const & i_xName) : m_wRep(i_pRep) , m_pRep(i_pRep) , m_xName(i_xName) { }; // css::rdf::XNode: virtual OUString SAL_CALL getStringValue() override; // css::rdf::XURI: virtual OUString SAL_CALL getNamespace() override; virtual OUString SAL_CALL getLocalName() override; // css::rdf::XNamedGraph: virtual uno::Reference SAL_CALL getName() override; virtual void SAL_CALL clear() override; virtual void SAL_CALL addStatement( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) override; virtual void SAL_CALL removeStatements( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) override; virtual uno::Reference< container::XEnumeration > SAL_CALL getStatements( 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_NoLock( 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. std::map> m_aStatementsCache; std::mutex m_CacheMutex; }; // css::rdf::XNode: OUString SAL_CALL librdf_NamedGraph::getStringValue() { return m_xName->getStringValue(); } // css::rdf::XURI: OUString SAL_CALL librdf_NamedGraph::getNamespace() { return m_xName->getNamespace(); } OUString SAL_CALL librdf_NamedGraph::getLocalName() { return m_xName->getLocalName(); } // css::rdf::XNamedGraph: uno::Reference< rdf::XURI > SAL_CALL librdf_NamedGraph::getName() { return m_xName; } void SAL_CALL librdf_NamedGraph::clear() { uno::Reference< rdf::XRepository > xRep( m_wRep ); if (!xRep.is()) { throw rdf::RepositoryException( "librdf_NamedGraph::clear: repository is gone", *this); } const OUString contextU( m_xName->getStringValue() ); try { m_pRep->clearGraph_NoLock(contextU); } catch (lang::IllegalArgumentException & ex) { css::uno::Any anyEx = cppu::getCaughtException(); throw lang::WrappedTargetRuntimeException( ex.Message, *this, anyEx ); } std::unique_lock g(m_CacheMutex); m_aStatementsCache.clear(); } void SAL_CALL librdf_NamedGraph::addStatement( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) { uno::Reference< rdf::XRepository > xRep( m_wRep ); if (!xRep.is()) { throw rdf::RepositoryException( "librdf_NamedGraph::addStatement: repository is gone", *this); } { std::unique_lock g(m_CacheMutex); m_aStatementsCache.clear(); } m_pRep->addStatementGraph_NoLock( i_xSubject, i_xPredicate, i_xObject, m_xName); } void SAL_CALL librdf_NamedGraph::removeStatements( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) { uno::Reference< rdf::XRepository > xRep( m_wRep ); if (!xRep.is()) { throw rdf::RepositoryException( "librdf_NamedGraph::removeStatements: repository is gone", *this); } { std::unique_lock g(m_CacheMutex); m_aStatementsCache.clear(); } m_pRep->removeStatementsGraph_NoLock( i_xSubject, i_xPredicate, i_xObject, m_xName); } OUString librdf_NamedGraph::createCacheKey_NoLock( 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_NoLock(i_xSubject, cacheKey); cacheKey.append("\t"); librdf_TypeConverter::extractResourceToCacheKey_NoLock(i_xPredicate, cacheKey); cacheKey.append("\t"); librdf_TypeConverter::extractNodeToCacheKey_NoLock(i_xObject, cacheKey); return cacheKey.makeStringAndClear(); } uno::Reference< container::XEnumeration > SAL_CALL librdf_NamedGraph::getStatements( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) { OUString cacheKey = createCacheKey_NoLock(i_xSubject, i_xPredicate, i_xObject); { std::unique_lock g(m_CacheMutex); auto it = m_aStatementsCache.find(cacheKey); if (it != m_aStatementsCache.end()) { return new librdf_GraphResult2(it->second); } } uno::Reference< rdf::XRepository > xRep( m_wRep ); if (!xRep.is()) { throw rdf::RepositoryException( "librdf_NamedGraph::getStatements: repository is gone", *this); } std::vector vStatements = m_pRep->getStatementsGraph_NoLock( i_xSubject, i_xPredicate, i_xObject, m_xName); { std::unique_lock g(m_CacheMutex); m_aStatementsCache.emplace(cacheKey, vStatements); } return new librdf_GraphResult2(std::move(vStatements)); } std::shared_ptr librdf_Repository::m_pWorld; sal_uInt32 librdf_Repository::m_NumInstances = 0; std::mutex librdf_Repository::m_aMutex; librdf_Repository::librdf_Repository( uno::Reference< uno::XComponentContext > const & i_xContext) : /*BaseMutex(),*/ m_xContext(i_xContext) // m_pWorld (static_cast(0), safe_librdf_free_world ), , m_pStorage(static_cast(nullptr), safe_librdf_free_storage) , m_pModel (static_cast(nullptr), safe_librdf_free_model ) , m_TypeConverter(i_xContext, *this) { OSL_ENSURE(i_xContext.is(), "librdf_Repository: null context"); std::scoped_lock g(m_aMutex); if (!m_NumInstances++) { m_pWorld.reset(m_TypeConverter.createWorld_Lock(), safe_librdf_free_world); } } librdf_Repository::~librdf_Repository() { std::scoped_lock g(m_aMutex); // must destroy these before world! m_pModel.reset(); m_pStorage.reset(); // FIXME: so it turns out that calling librdf_free_world will // (via raptor_sax2_finish) call xmlCleanupParser, which will // free libxml2's globals! ARRRGH!!! => never call librdf_free_world #if 0 if (!--m_NumInstances) { m_pWorld.reset(); } #endif } // com.sun.star.uno.XServiceInfo: OUString SAL_CALL librdf_Repository::getImplementationName() { return "librdf_Repository"; } sal_Bool SAL_CALL librdf_Repository::supportsService( OUString const & serviceName) { return cppu::supportsService(this, serviceName); } uno::Sequence< OUString > SAL_CALL librdf_Repository::getSupportedServiceNames() { return { "com.sun.star.rdf.Repository" }; } // css::rdf::XRepository: uno::Reference< rdf::XBlankNode > SAL_CALL librdf_Repository::createBlankNode() { std::scoped_lock g(m_aMutex); const std::shared_ptr pNode( librdf_new_node_from_blank_identifier(m_pWorld.get(), nullptr), safe_librdf_free_node); if (!pNode) { throw uno::RuntimeException( "librdf_Repository::createBlankNode: " "librdf_new_node_from_blank_identifier failed", *this); } const unsigned char * id (librdf_node_get_blank_identifier(pNode.get())); if (!id) { throw uno::RuntimeException( "librdf_Repository::createBlankNode: " "librdf_node_get_blank_identifier failed", *this); } const OUString nodeID(OUString::createFromAscii( reinterpret_cast(id))); try { return rdf::BlankNode::create(m_xContext, nodeID); } catch (const lang::IllegalArgumentException &) { css::uno::Any anyEx = cppu::getCaughtException(); throw lang::WrappedTargetRuntimeException( "librdf_Repository::createBlankNode: " "illegal blank node label", *this, anyEx); } } //void SAL_CALL uno::Reference SAL_CALL librdf_Repository::importGraph(::sal_Int16 i_Format, const uno::Reference< io::XInputStream > & i_xInStream, const uno::Reference< rdf::XURI > & i_xGraphName, const uno::Reference< rdf::XURI > & i_xBaseURI) { if (!i_xInStream.is()) { throw lang::IllegalArgumentException( "librdf_Repository::importGraph: stream is null", *this, 1); } //FIXME: other formats if (i_Format != rdf::FileFormat::RDF_XML) { throw datatransfer::UnsupportedFlavorException( "librdf_Repository::importGraph: file format not supported", *this); } if (!i_xGraphName.is()) { throw lang::IllegalArgumentException( "librdf_Repository::importGraph: graph name is null", *this, 2); } if (i_xGraphName->getStringValue().startsWith(s_nsOOo)) { throw lang::IllegalArgumentException( "librdf_Repository::importGraph: URI is reserved", *this, 0); } if (!i_xBaseURI.is()) { //FIXME: any i_Format that don't need a base URI? throw lang::IllegalArgumentException( "librdf_Repository::importGraph: base URI is null", *this, 3); } OSL_ENSURE(i_xBaseURI.is(), "no base uri"); const OUString baseURIU( i_xBaseURI->getStringValue() ); if (baseURIU.indexOf('#') >= 0) { throw lang::IllegalArgumentException( "librdf_Repository::importGraph: base URI is not absolute", *this, 3); } const OUString contextU( i_xGraphName->getStringValue() ); uno::Sequence buf; uno::Reference xSeekable(i_xInStream, uno::UNO_QUERY); // UGLY: if only redland could read streams... const sal_Int64 sz( xSeekable.is() ? xSeekable->getLength() : 1 << 20 ); // exceptions are propagated i_xInStream->readBytes( buf, static_cast( sz ) ); std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked if (m_NamedGraphs.find(contextU) != m_NamedGraphs.end()) { throw container::ElementExistException( "librdf_Repository::importGraph: graph with given URI exists", *this); } const OString context( OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pContext( librdf_new_node_from_uri_string(m_pWorld.get(), reinterpret_cast (context.getStr())), safe_librdf_free_node); if (!pContext) { throw uno::RuntimeException( "librdf_Repository::importGraph: librdf_new_node_from_uri_string failed", *this); } const OString baseURI( OUStringToOString(baseURIU, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pBaseURI( librdf_new_uri(m_pWorld.get(), reinterpret_cast (baseURI.getStr())), safe_librdf_free_uri); if (!pBaseURI) { throw uno::RuntimeException( "librdf_Repository::importGraph: librdf_new_uri failed", *this); } const std::shared_ptr pParser( librdf_new_parser(m_pWorld.get(), "rdfxml", nullptr, nullptr), safe_librdf_free_parser); if (!pParser) { throw uno::RuntimeException( "librdf_Repository::importGraph: " "librdf_new_parser failed", *this); } const std::shared_ptr pStream( librdf_parser_parse_counted_string_as_stream(pParser.get(), reinterpret_cast(buf.getConstArray()), buf.getLength(), pBaseURI.get()), safe_librdf_free_stream); if (!pStream) { throw rdf::ParseException( "librdf_Repository::importGraph: " "librdf_parser_parse_counted_string_as_stream failed", *this); } rtl::Reference const pGraph( new librdf_NamedGraph(this, i_xGraphName)); m_NamedGraphs.insert(std::make_pair(contextU, pGraph)); if (librdf_model_context_add_statements(m_pModel.get(), pContext.get(), pStream.get())) { throw rdf::RepositoryException( "librdf_Repository::importGraph: " "librdf_model_context_add_statements failed", *this); } return pGraph; } void addChaffWhenEncryptedStorage(const uno::Reference< io::XOutputStream > &rStream, unsigned char* pBuffer, size_t length) { if (!length) return; uno::Reference< embed::XEncryptionProtectedSource2 > xEncr(rStream, uno::UNO_QUERY); bool bAddChaff = xEncr.is() && xEncr->hasEncryptionData(); // exceptions are propagated if (!bAddChaff) { const uno::Sequence buf( reinterpret_cast(pBuffer), length); rStream->writeBytes(buf); } else { unsigned char *postcomment = reinterpret_cast(strchr(reinterpret_cast(pBuffer), '\n')); if (postcomment != nullptr) { ++postcomment; size_t preamblelen = postcomment - pBuffer; uno::Sequence buf( reinterpret_cast(pBuffer), preamblelen); rStream->writeBytes(buf); OString aComment = ""; buf = uno::Sequence( reinterpret_cast(aComment.getStr()), aComment.getLength()); rStream->writeBytes(buf); buf = uno::Sequence( reinterpret_cast(postcomment), length-preamblelen); rStream->writeBytes(buf); } } } void SAL_CALL librdf_Repository::exportGraph(::sal_Int16 i_Format, const uno::Reference< io::XOutputStream > & i_xOutStream, const uno::Reference< rdf::XURI > & i_xGraphName, const uno::Reference< rdf::XURI > & i_xBaseURI) { if (!i_xOutStream.is()) { throw lang::IllegalArgumentException( "librdf_Repository::exportGraph: stream is null", *this, 1); } // FIXME: other formats if (i_Format != rdf::FileFormat::RDF_XML) { throw datatransfer::UnsupportedFlavorException( "librdf_Repository::exportGraph: " "file format not supported", *this); } if (!i_xGraphName.is()) { throw lang::IllegalArgumentException( "librdf_Repository::exportGraph: " "graph name is null", *this, 2); } if (!i_xBaseURI.is()) { //FIXME: any i_Format that don't need a base URI? throw lang::IllegalArgumentException( "librdf_Repository::exportGraph: " "base URI is null", *this, 3); } OSL_ENSURE(i_xBaseURI.is(), "no base uri"); const OUString baseURIU( i_xBaseURI->getStringValue() ); if (baseURIU.indexOf('#') >= 0) { throw lang::IllegalArgumentException( "librdf_Repository::exportGraph: " "base URI is not absolute", *this, 3); } const OUString contextU( i_xGraphName->getStringValue() ); std::unique_lock g(m_aMutex); // don't call i_x* with mutex locked if (m_NamedGraphs.find(contextU) == m_NamedGraphs.end()) { throw container::NoSuchElementException( "librdf_Repository::exportGraph: " "no graph with given URI exists", *this); } const OString context( OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pContext( librdf_new_node_from_uri_string(m_pWorld.get(), reinterpret_cast (context.getStr())), safe_librdf_free_node); if (!pContext) { throw uno::RuntimeException( "librdf_Repository::exportGraph: " "librdf_new_node_from_uri_string failed", *this); } const OString baseURI( OUStringToOString(baseURIU, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pBaseURI( librdf_new_uri(m_pWorld.get(), reinterpret_cast (baseURI.getStr())), safe_librdf_free_uri); if (!pBaseURI) { throw uno::RuntimeException( "librdf_Repository::exportGraph: " "librdf_new_uri failed", *this); } const std::shared_ptr pStream( librdf_model_context_as_stream(m_pModel.get(), pContext.get()), safe_librdf_free_stream); if (!pStream) { throw rdf::RepositoryException( "librdf_Repository::exportGraph: " "librdf_model_context_as_stream failed", *this); } const char * const format("rdfxml"); // #i116443#: abbrev breaks when certain URIs are used as data types // const char *format("rdfxml-abbrev"); const std::shared_ptr pSerializer( librdf_new_serializer(m_pWorld.get(), format, nullptr, nullptr), safe_librdf_free_serializer); if (!pSerializer) { throw uno::RuntimeException( "librdf_Repository::exportGraph: " "librdf_new_serializer failed", *this); } const std::shared_ptr pRelativeURI( librdf_new_uri(m_pWorld.get(), reinterpret_cast ("http://feature.librdf.org/raptor-relativeURIs")), safe_librdf_free_uri); const std::shared_ptr pWriteBaseURI( librdf_new_uri(m_pWorld.get(), reinterpret_cast ("http://feature.librdf.org/raptor-writeBaseURI")), safe_librdf_free_uri); const std::shared_ptr p0( librdf_new_node_from_literal(m_pWorld.get(), reinterpret_cast ("0"), nullptr, 0), safe_librdf_free_node); const std::shared_ptr p1( librdf_new_node_from_literal(m_pWorld.get(), reinterpret_cast ("1"), nullptr, 0), safe_librdf_free_node); if (!pWriteBaseURI || !pRelativeURI || !p0 || !p1) { throw uno::RuntimeException( "librdf_Repository::exportGraph: " "librdf_new_uri or librdf_new_node_from_literal failed", *this); } // make URIs relative to base URI if (librdf_serializer_set_feature(pSerializer.get(), pRelativeURI.get(), p1.get())) { throw uno::RuntimeException( "librdf_Repository::exportGraph: " "librdf_serializer_set_feature relativeURIs failed", *this); } // but do not write the base URI to the file! if (librdf_serializer_set_feature(pSerializer.get(), pWriteBaseURI.get(), p0.get())) { throw uno::RuntimeException( "librdf_Repository::exportGraph: " "librdf_serializer_set_feature writeBaseURI failed", *this); } size_t length; const std::shared_ptr pBuf( librdf_serializer_serialize_stream_to_counted_string( pSerializer.get(), pBaseURI.get(), pStream.get(), &length), free); if (!pBuf) { throw rdf::RepositoryException( "librdf_Repository::exportGraph: " "librdf_serializer_serialize_stream_to_counted_string failed", *this); } g.unlock(); // release Mutex before calling i_xOutStream methods addChaffWhenEncryptedStorage(i_xOutStream, pBuf.get(), length); } uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL librdf_Repository::getGraphNames() { std::scoped_lock g(m_aMutex); ::std::vector< uno::Reference > ret; std::transform(m_NamedGraphs.begin(), m_NamedGraphs.end(), std::back_inserter(ret), [](std::pair> const& it) { return it.second->getName(); }); return comphelper::containerToSequence(ret); } uno::Reference< rdf::XNamedGraph > SAL_CALL librdf_Repository::getGraph(const uno::Reference< rdf::XURI > & i_xGraphName) { if (!i_xGraphName.is()) { throw lang::IllegalArgumentException( "librdf_Repository::getGraph: URI is null", *this, 0); } const OUString contextU( i_xGraphName->getStringValue() ); std::scoped_lock g(m_aMutex); const NamedGraphMap_t::iterator iter( m_NamedGraphs.find(contextU) ); if (iter != m_NamedGraphs.end()) { return iter->second; } else { return nullptr; } } uno::Reference< rdf::XNamedGraph > SAL_CALL librdf_Repository::createGraph(const uno::Reference< rdf::XURI > & i_xGraphName) { if (!i_xGraphName.is()) { throw lang::IllegalArgumentException( "librdf_Repository::createGraph: URI is null", *this, 0); } const OUString contextU( i_xGraphName->getStringValue() ); if (contextU.startsWith(s_nsOOo)) { throw lang::IllegalArgumentException( "librdf_Repository::createGraph: URI is reserved", *this, 0); } std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked // NB: librdf does not have a concept of graphs as such; // a librdf named graph exists iff the model contains a statement with // the graph name as context if (m_NamedGraphs.find(contextU) != m_NamedGraphs.end()) { throw container::ElementExistException( "librdf_Repository::createGraph: graph with given URI exists", *this); } m_NamedGraphs.insert(std::make_pair(contextU, new librdf_NamedGraph(this, i_xGraphName))); return m_NamedGraphs.find(contextU)->second; } void SAL_CALL librdf_Repository::destroyGraph( const uno::Reference< rdf::XURI > & i_xGraphName) { if (!i_xGraphName.is()) { throw lang::IllegalArgumentException( "librdf_Repository::destroyGraph: URI is null", *this, 0); } const OUString contextU( i_xGraphName->getStringValue() ); std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked const NamedGraphMap_t::iterator iter( clearGraph_Lock(contextU, false) ); m_NamedGraphs.erase(iter); } bool isMetadatableWithoutMetadata( uno::Reference const & i_xNode) { const uno::Reference xMeta( i_xNode, uno::UNO_QUERY ); return (xMeta.is() && xMeta->getMetadataReference().Second.isEmpty()); } uno::Reference< container::XEnumeration > SAL_CALL librdf_Repository::getStatements( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) { if (isMetadatableWithoutMetadata(i_xSubject) || isMetadatableWithoutMetadata(i_xPredicate) || isMetadatableWithoutMetadata(i_xObject)) { return new librdf_GraphResult(this, m_aMutex, std::shared_ptr(), std::shared_ptr()); } librdf_TypeConverter::Statement const stmt( librdf_TypeConverter::extractStatement_NoLock( i_xSubject, i_xPredicate, i_xObject)); std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked const std::shared_ptr pStatement( librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt), safe_librdf_free_statement); OSL_ENSURE(pStatement, "mkStatement failed"); const std::shared_ptr pStream( librdf_model_find_statements(m_pModel.get(), pStatement.get()), safe_librdf_free_stream); if (!pStream) { throw rdf::RepositoryException( "librdf_Repository::getStatements: " "librdf_model_find_statements failed", *this); } return new librdf_GraphResult(this, m_aMutex, pStream, std::shared_ptr()); } uno::Reference< rdf::XQuerySelectResult > SAL_CALL librdf_Repository::querySelect(const OUString & i_rQuery) { std::scoped_lock g(m_aMutex); const OString query( OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pQuery( librdf_new_query(m_pWorld.get(), s_sparql, nullptr, reinterpret_cast (query.getStr()), nullptr), safe_librdf_free_query); if (!pQuery) { throw rdf::QueryException( "librdf_Repository::querySelect: " "librdf_new_query failed", *this); } const std::shared_ptr pResults( librdf_model_query_execute(m_pModel.get(), pQuery.get()), safe_librdf_free_query_results); if (!pResults || !librdf_query_results_is_bindings(pResults.get())) { throw rdf::QueryException( "librdf_Repository::querySelect: " "query result is null or not bindings", *this); } const int count( librdf_query_results_get_bindings_count(pResults.get()) ); if (count < 0) { throw rdf::QueryException( "librdf_Repository::querySelect: " "librdf_query_results_get_bindings_count failed", *this); } uno::Sequence< OUString > names(count); auto namesRange = asNonConstRange(names); for (int i = 0; i < count; ++i) { const char* name( librdf_query_results_get_binding_name( pResults.get(), i) ); if (!name) { throw rdf::QueryException( "librdf_Repository::querySelect: binding is null", *this); } namesRange[i] = OUString::createFromAscii(name); } return new librdf_QuerySelectResult(this, m_aMutex, pQuery, pResults, names); } uno::Reference< container::XEnumeration > SAL_CALL librdf_Repository::queryConstruct(const OUString & i_rQuery) { std::scoped_lock g(m_aMutex); const OString query( OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pQuery( librdf_new_query(m_pWorld.get(), s_sparql, nullptr, reinterpret_cast (query.getStr()), nullptr), safe_librdf_free_query); if (!pQuery) { throw rdf::QueryException( "librdf_Repository::queryConstruct: " "librdf_new_query failed", *this); } const std::shared_ptr pResults( librdf_model_query_execute(m_pModel.get(), pQuery.get()), safe_librdf_free_query_results); if (!pResults || !librdf_query_results_is_graph(pResults.get())) { throw rdf::QueryException( "librdf_Repository::queryConstruct: " "query result is null or not graph", *this); } const std::shared_ptr pStream( librdf_query_results_as_stream(pResults.get()), safe_librdf_free_stream); if (!pStream) { throw rdf::QueryException( "librdf_Repository::queryConstruct: " "librdf_query_results_as_stream failed", *this); } return new librdf_GraphResult(this, m_aMutex, pStream, std::shared_ptr(), pQuery); } sal_Bool SAL_CALL librdf_Repository::queryAsk(const OUString & i_rQuery) { std::scoped_lock g(m_aMutex); const OString query( OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pQuery( librdf_new_query(m_pWorld.get(), s_sparql, nullptr, reinterpret_cast (query.getStr()), nullptr), safe_librdf_free_query); if (!pQuery) { throw rdf::QueryException( "librdf_Repository::queryAsk: " "librdf_new_query failed", *this); } const std::shared_ptr pResults( librdf_model_query_execute(m_pModel.get(), pQuery.get()), safe_librdf_free_query_results); if (!pResults || !librdf_query_results_is_boolean(pResults.get())) { throw rdf::QueryException( "librdf_Repository::queryAsk: " "query result is null or not boolean", *this); } return bool(librdf_query_results_get_boolean(pResults.get())); } // css::rdf::XDocumentRepository: void SAL_CALL librdf_Repository::setStatementRDFa( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Sequence< uno::Reference< rdf::XURI > > & i_rPredicates, const uno::Reference< rdf::XMetadatable > & i_xObject, const OUString & i_rRDFaContent, const uno::Reference< rdf::XURI > & i_xRDFaDatatype) { if (!i_xSubject.is()) { throw lang::IllegalArgumentException( "librdf_Repository::setStatementRDFa: Subject is null", *this, 0); } if (!i_rPredicates.hasElements()) { throw lang::IllegalArgumentException( "librdf_Repository::setStatementRDFa: no Predicates", *this, 1); } if (std::any_of(i_rPredicates.begin(), i_rPredicates.end(), [](const uno::Reference< rdf::XURI >& rPredicate) { return !rPredicate.is(); })) { throw lang::IllegalArgumentException( "librdf_Repository::setStatementRDFa: Predicate is null", *this, 1); } if (!i_xObject.is()) { throw lang::IllegalArgumentException( "librdf_Repository::setStatementRDFa: Object is null", *this, 2); } const uno::Reference xService(i_xObject, uno::UNO_QUERY_THROW); uno::Reference xTextRange; if (xService->supportsService("com.sun.star.table.Cell") || xService->supportsService("com.sun.star.text.CellProperties") || // for writer xService->supportsService("com.sun.star.text.Paragraph")) { xTextRange.set(i_xObject, uno::UNO_QUERY_THROW); } else if (xService->supportsService("com.sun.star.text.Bookmark") || xService->supportsService("com.sun.star.text.InContentMetadata")) { const uno::Reference xTextContent(i_xObject, uno::UNO_QUERY_THROW); xTextRange = xTextContent->getAnchor(); } if (!xTextRange.is()) { throw lang::IllegalArgumentException( "librdf_Repository::setStatementRDFa: " "Object does not support RDFa", *this, 2); } // ensure that the metadatable has an XML ID i_xObject->ensureMetadataReference(); const beans::StringPair mdref( i_xObject->getMetadataReference() ); if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) { throw uno::RuntimeException( "librdf_Repository::setStatementRDFa: " "ensureMetadataReference did not", *this); } OUString const sXmlId(mdref.First + "#" + mdref.Second); OUString const sContext(s_nsOOo + sXmlId); OUString const content( (i_rRDFaContent.isEmpty()) ? xTextRange->getString() : i_rRDFaContent ); uno::Reference xContent; try { if (i_xRDFaDatatype.is()) { xContent.set(rdf::Literal::createWithType(m_xContext, content, i_xRDFaDatatype), uno::UNO_QUERY_THROW); } else { xContent.set(rdf::Literal::create(m_xContext, content), uno::UNO_QUERY_THROW); } } catch (const lang::IllegalArgumentException &) { css::uno::Any anyEx = cppu::getCaughtException(); throw lang::WrappedTargetRuntimeException( "librdf_Repository::setStatementRDFa: " "cannot create literal", *this, anyEx); } std::shared_ptr const pSubject( librdf_TypeConverter::extractResource_NoLock(i_xSubject)); std::shared_ptr const pContent( librdf_TypeConverter::extractNode_NoLock(xContent)); ::std::vector< std::shared_ptr > predicates; ::std::transform(i_rPredicates.begin(), i_rPredicates.end(), ::std::back_inserter(predicates), [](uno::Reference const& xURI) { return librdf_TypeConverter::extractResource_NoLock(xURI); }); removeStatementRDFa(i_xObject); // not atomic with insertion? std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked if (i_rRDFaContent.isEmpty()) { m_RDFaXHTMLContentSet.erase(sXmlId); } else { m_RDFaXHTMLContentSet.insert(sXmlId); } try { for (const auto& rPredicatePtr : predicates) { addStatementGraph_Lock( librdf_TypeConverter::Statement(pSubject, std::dynamic_pointer_cast(rPredicatePtr), pContent), sContext, true); } } catch (const container::NoSuchElementException&) { css::uno::Any anyEx = cppu::getCaughtException(); throw lang::WrappedTargetRuntimeException( "librdf_Repository::setStatementRDFa: " "cannot addStatementGraph", *this, anyEx); } } void SAL_CALL librdf_Repository::removeStatementRDFa( const uno::Reference< rdf::XMetadatable > & i_xElement) { if (!i_xElement.is()) { throw lang::IllegalArgumentException( "librdf_Repository::removeStatementRDFa: Element is null", *this, 0); } const beans::StringPair mdref( i_xElement->getMetadataReference() ); if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) { return; // nothing to do... } OUString const sXmlId(s_nsOOo + mdref.First + "#" + mdref.Second); clearGraph_NoLock(sXmlId, true); } beans::Pair< uno::Sequence, sal_Bool > SAL_CALL librdf_Repository::getStatementRDFa( const uno::Reference< rdf::XMetadatable > & i_xElement) { if (!i_xElement.is()) { throw lang::IllegalArgumentException( "librdf_Repository::getStatementRDFa: Element is null", *this, 0); } const beans::StringPair mdref( i_xElement->getMetadataReference() ); if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) { return beans::Pair< uno::Sequence, sal_Bool >(); } OUString const sXmlId(mdref.First + "#" + mdref.Second); uno::Reference xXmlId; try { xXmlId.set( rdf::URI::create(m_xContext, s_nsOOo + sXmlId), uno::UNO_SET_THROW); } catch (const lang::IllegalArgumentException &) { css::uno::Any anyEx = cppu::getCaughtException(); throw lang::WrappedTargetRuntimeException( "librdf_Repository::getStatementRDFa: " "cannot create URI for XML ID", *this, anyEx); } ::std::vector< rdf::Statement > ret; try { ret = getStatementsGraph_NoLock(nullptr, nullptr, nullptr, xXmlId, true); } catch (const container::NoSuchElementException&) { css::uno::Any anyEx = cppu::getCaughtException(); throw lang::WrappedTargetRuntimeException( "librdf_Repository::getStatementRDFa: " "cannot getStatementsGraph", *this, anyEx); } std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked return beans::Pair< uno::Sequence, sal_Bool >( comphelper::containerToSequence(ret), 0 != m_RDFaXHTMLContentSet.count(sXmlId)); } extern "C" librdf_statement *rdfa_context_stream_map_handler( librdf_stream *i_pStream, void *, librdf_statement *i_pStatement) { OSL_ENSURE(i_pStream, "rdfa_context_stream_map_handler: stream null"); if (i_pStream) { librdf_node *pCtxt( #if LIBRDF_VERSION >= 10012 librdf_stream_get_context2(i_pStream) ); #else static_cast(librdf_stream_get_context(i_pStream)) ); #endif OSL_ENSURE(pCtxt, "rdfa_context_stream_map_handler: context null"); if (pCtxt && isInternalContext(pCtxt)) { return i_pStatement; } } return nullptr; }; uno::Reference< container::XEnumeration > SAL_CALL librdf_Repository::getStatementsRDFa( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) { if (isMetadatableWithoutMetadata(i_xSubject) || isMetadatableWithoutMetadata(i_xPredicate) || isMetadatableWithoutMetadata(i_xObject)) { return new librdf_GraphResult(this, m_aMutex, std::shared_ptr(), std::shared_ptr()); } librdf_TypeConverter::Statement const stmt( librdf_TypeConverter::extractStatement_NoLock( i_xSubject, i_xPredicate, i_xObject)); std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked const std::shared_ptr pStatement( librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt), safe_librdf_free_statement); OSL_ENSURE(pStatement, "mkStatement failed"); const std::shared_ptr pStream( librdf_model_find_statements(m_pModel.get(), pStatement.get()), safe_librdf_free_stream); if (!pStream) { throw rdf::RepositoryException( "librdf_Repository::getStatementsRDFa: " "librdf_model_find_statements failed", *this); } if (librdf_stream_add_map(pStream.get(), rdfa_context_stream_map_handler, nullptr, nullptr)) { throw rdf::RepositoryException( "librdf_Repository::getStatementsRDFa: " "librdf_stream_add_map failed", *this); } return new librdf_GraphResult(this, m_aMutex, pStream, std::shared_ptr()); } // css::lang::XInitialization: void SAL_CALL librdf_Repository::initialize( const uno::Sequence< css::uno::Any > &) { std::scoped_lock g(m_aMutex); // m_pWorld.reset(m_TypeConverter.createWorld(), safe_librdf_free_world); m_pStorage.reset(m_TypeConverter.createStorage_Lock(m_pWorld.get()), safe_librdf_free_storage); m_pModel.reset(m_TypeConverter.createModel_Lock( m_pWorld.get(), m_pStorage.get()), safe_librdf_free_model); } NamedGraphMap_t::iterator librdf_Repository::clearGraph_NoLock( OUString const& i_rGraphName, bool i_Internal) // throw (uno::RuntimeException, container::NoSuchElementException, // rdf::RepositoryException) { std::scoped_lock g(m_aMutex); return clearGraph_Lock(i_rGraphName, i_Internal); } NamedGraphMap_t::iterator librdf_Repository::clearGraph_Lock( OUString const& i_rGraphName, bool i_Internal) { // internal: must be called with mutex locked! const NamedGraphMap_t::iterator iter( m_NamedGraphs.find(i_rGraphName) ); if (!i_Internal && iter == m_NamedGraphs.end()) { throw container::NoSuchElementException( "librdf_Repository::clearGraph: " "no graph with given URI exists", *this); } const OString context( OUStringToOString(i_rGraphName, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pContext( librdf_new_node_from_uri_string(m_pWorld.get(), reinterpret_cast (context.getStr())), safe_librdf_free_node); if (!pContext) { throw uno::RuntimeException( "librdf_Repository::clearGraph: " "librdf_new_node_from_uri_string failed", *this); } if (librdf_model_context_remove_statements(m_pModel.get(), pContext.get())) { throw rdf::RepositoryException( "librdf_Repository::clearGraph: " "librdf_model_context_remove_statements failed", *this); } return iter; } void librdf_Repository::addStatementGraph_NoLock( 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) //throw (uno::RuntimeException, lang::IllegalArgumentException, // container::NoSuchElementException, rdf::RepositoryException) { if (!i_xSubject.is()) { throw lang::IllegalArgumentException( "librdf_Repository::addStatement: Subject is null", *this, 0); } if (!i_xPredicate.is()) { throw lang::IllegalArgumentException( "librdf_Repository::addStatement: Predicate is null", *this, 1); } if (!i_xObject.is()) { throw lang::IllegalArgumentException( "librdf_Repository::addStatement: Object is null", *this, 2); } librdf_TypeConverter::Statement const stmt( librdf_TypeConverter::extractStatement_NoLock( i_xSubject, i_xPredicate, i_xObject)); const OUString contextU( i_xGraphName->getStringValue() ); std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked addStatementGraph_Lock(stmt, contextU, false/*i_Internal*/); } void librdf_Repository::addStatementGraph_Lock( librdf_TypeConverter::Statement const& i_rStatement, OUString const& i_rGraphName, bool i_Internal) { if (!i_Internal && (m_NamedGraphs.find(i_rGraphName) == m_NamedGraphs.end())) { throw container::NoSuchElementException( "librdf_Repository::addStatement: " "no graph with given URI exists", *this); } const OString context( OUStringToOString(i_rGraphName, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pContext( librdf_new_node_from_uri_string(m_pWorld.get(), reinterpret_cast (context.getStr())), safe_librdf_free_node); if (!pContext) { throw uno::RuntimeException( "librdf_Repository::addStatement: " "librdf_new_node_from_uri_string failed", *this); } const std::shared_ptr pStatement( librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), i_rStatement), safe_librdf_free_statement); OSL_ENSURE(pStatement, "mkStatement failed"); // Test for duplicate statement // librdf_model_add_statement disallows duplicates while // librdf_model_context_add_statement allows duplicates { const std::shared_ptr pStream( librdf_model_find_statements_in_context(m_pModel.get(), pStatement.get(), pContext.get()), safe_librdf_free_stream); if (pStream && !librdf_stream_end(pStream.get())) return; } if (librdf_model_context_add_statement(m_pModel.get(), pContext.get(), pStatement.get())) { throw rdf::RepositoryException( "librdf_Repository::addStatement: " "librdf_model_context_add_statement failed", *this); } } void librdf_Repository::removeStatementsGraph_NoLock( 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) //throw (uno::RuntimeException, lang::IllegalArgumentException, // container::NoSuchElementException, rdf::RepositoryException) { if (isMetadatableWithoutMetadata(i_xSubject) || isMetadatableWithoutMetadata(i_xPredicate) || isMetadatableWithoutMetadata(i_xObject)) { return; } librdf_TypeConverter::Statement const stmt( librdf_TypeConverter::extractStatement_NoLock( i_xSubject, i_xPredicate, i_xObject)); const OUString contextU( i_xGraphName->getStringValue() ); std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked if (m_NamedGraphs.find(contextU) == m_NamedGraphs.end()) { throw container::NoSuchElementException( "librdf_Repository::removeStatements: " "no graph with given URI exists", *this); } const OString context( OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) ); const std::shared_ptr pContext( librdf_new_node_from_uri_string(m_pWorld.get(), reinterpret_cast (context.getStr())), safe_librdf_free_node); if (!pContext) { throw uno::RuntimeException( "librdf_Repository::removeStatements: " "librdf_new_node_from_uri_string failed", *this); } const std::shared_ptr pStatement( librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt), safe_librdf_free_statement); OSL_ENSURE(pStatement, "mkStatement failed"); const std::shared_ptr 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::removeStatements: " "librdf_model_find_statements_in_context failed", *this); } if (librdf_stream_end(pStream.get())) return; do { librdf_statement *pStmt( librdf_stream_get_object(pStream.get()) ); if (!pStmt) { throw rdf::RepositoryException( "librdf_Repository::removeStatements: " "librdf_stream_get_object failed", *this); } if (librdf_model_context_remove_statement(m_pModel.get(), pContext.get(), pStmt)) { throw rdf::RepositoryException( "librdf_Repository::removeStatements: " "librdf_model_context_remove_statement failed", *this); } } while (!librdf_stream_next(pStream.get())); } std::vector librdf_Repository::getStatementsGraph_NoLock( 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 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() ); std::scoped_lock 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 pContext( librdf_new_node_from_uri_string(m_pWorld.get(), reinterpret_cast (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 pStatement( librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt), safe_librdf_free_statement); OSL_ENSURE(pStatement, "mkStatement failed"); const std::shared_ptr 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_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::Any(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.emplace_back( getTypeConverter().convertToStatement(pStmt, pCtxt) ); // NB: this will invalidate current item. librdf_stream_next(pStream.get()); } return ret; } extern "C" void librdf_raptor_init(void* /*user_data*/, raptor_world* pRaptorWorld) { // fdo#64672 prevent raptor from setting global libxml2 error handlers raptor_world_set_flag(pRaptorWorld, RAPTOR_WORLD_FLAG_LIBXML_STRUCTURED_ERROR_SAVE, 0); raptor_world_set_flag(pRaptorWorld, RAPTOR_WORLD_FLAG_LIBXML_GENERIC_ERROR_SAVE, 0); } librdf_world *librdf_TypeConverter::createWorld_Lock() const { // create and initialize world librdf_world *pWorld( librdf_new_world() ); if (!pWorld) { throw uno::RuntimeException( "librdf_TypeConverter::createWorld: librdf_new_world failed", m_rRep); } librdf_world_set_raptor_init_handler(pWorld, nullptr, &librdf_raptor_init); //FIXME logger, digest, features? xsltSecurityPrefsPtr origprefs = xsltGetDefaultSecurityPrefs(); librdf_world_open(pWorld); xsltSecurityPrefsPtr newprefs = xsltGetDefaultSecurityPrefs(); if (newprefs != origprefs) { // #i110523# restore libxslt global configuration // (gratuitously overwritten by raptor_init_parser_grddl_common) // (this is the only reason unordf is linked against libxslt) xsltSetDefaultSecurityPrefs(origprefs); } return pWorld; } librdf_storage * librdf_TypeConverter::createStorage_Lock(librdf_world *i_pWorld) const { librdf_storage *pStorage( // librdf_new_storage(i_pWorld, "memory", NULL, "contexts='yes'") ); librdf_new_storage(i_pWorld, "hashes", nullptr, "contexts='yes',hash-type='memory'") ); if (!pStorage) { throw uno::RuntimeException( "librdf_TypeConverter::createStorage: librdf_new_storage failed", m_rRep); } return pStorage; } librdf_model *librdf_TypeConverter::createModel_Lock( librdf_world *i_pWorld, librdf_storage * i_pStorage) const { librdf_model *pRepository( librdf_new_model(i_pWorld, i_pStorage, nullptr) ); if (!pRepository) { throw uno::RuntimeException( "librdf_TypeConverter::createModel: librdf_new_model failed", m_rRep); } //FIXME #if 0 { librdf_uri * ctxt = librdf_new_uri(i_pWorld, reinterpret_cast(LIBRDF_MODEL_FEATURE_CONTEXTS)); librdf_node * contexts = librdf_model_get_feature(repository, ctxt); if (!contexts) throw; std::cout << "value of contexts feature: "; prtNode(contexts); std::cout << std::endl; // librdf_model_set_feature(repository, LIBRDF_FEATURE_CONTEXTS, ...); safe_librdf_free_node(contexts); safe_librdf_free_uri(ctxt); } #endif return pRepository; } // this does NOT create a node, only URI librdf_uri* librdf_TypeConverter::mkURI_Lock( librdf_world* i_pWorld, OString const& i_rURI) { librdf_uri *pURI( librdf_new_uri(i_pWorld, reinterpret_cast(i_rURI.getStr()))); if (!pURI) { throw uno::RuntimeException( "librdf_TypeConverter::mkURI: librdf_new_uri failed", nullptr); } return pURI; } // extract blank or URI node - call without Mutex locked std::shared_ptr librdf_TypeConverter::extractResource_NoLock( const uno::Reference< rdf::XResource > & i_xResource) { if (!i_xResource.is()) { return std::shared_ptr(); } uno::Reference< rdf::XBlankNode > xBlankNode(i_xResource, uno::UNO_QUERY); if (xBlankNode.is()) { const OString label( OUStringToOString(xBlankNode->getStringValue(), RTL_TEXTENCODING_UTF8) ); return std::make_shared(label); } else { // assumption: everything else is URI const OString uri( OUStringToOString(i_xResource->getStringValue(), RTL_TEXTENCODING_UTF8) ); return std::make_shared(uri); } } void librdf_TypeConverter::extractResourceToCacheKey_NoLock( 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 " + xBlankNode->getStringValue()); } else { // assumption: everything else is URI rBuffer.append("URI " + i_xResource->getStringValue()); } } // create blank or URI node librdf_node* librdf_TypeConverter::mkResource_Lock( librdf_world* i_pWorld, Resource const*const i_pResource) { if (!i_pResource) return nullptr; BlankNode const*const pBlankNode( dynamic_cast(i_pResource)); if (pBlankNode) { librdf_node *pNode( librdf_new_node_from_blank_identifier(i_pWorld, reinterpret_cast( pBlankNode->value.getStr()))); if (!pNode) { throw uno::RuntimeException( "librdf_TypeConverter::mkResource: " "librdf_new_node_from_blank_identifier failed", nullptr); } return pNode; } else { // assumption: everything else is URI URI const*const pURI(dynamic_cast(i_pResource)); assert(pURI); librdf_node *pNode( librdf_new_node_from_uri_string(i_pWorld, reinterpret_cast(pURI->value.getStr()))); if (!pNode) { throw uno::RuntimeException( "librdf_TypeConverter::mkResource: " "librdf_new_node_from_uri_string failed", nullptr); } return pNode; } } // extract blank or URI or literal node - call without Mutex locked std::shared_ptr librdf_TypeConverter::extractNode_NoLock( const uno::Reference< rdf::XNode > & i_xNode) { if (!i_xNode.is()) { return std::shared_ptr(); } uno::Reference< rdf::XResource > xResource(i_xNode, uno::UNO_QUERY); if (xResource.is()) { return extractResource_NoLock(xResource); } 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 std::shared_ptr(); } const OString val( OUStringToOString(xLiteral->getValue(), RTL_TEXTENCODING_UTF8) ); const OString lang( OUStringToOString(xLiteral->getLanguage(), RTL_TEXTENCODING_UTF8) ); const uno::Reference< rdf::XURI > xType(xLiteral->getDatatype()); std::optional type; if (xType.is()) { type = OUStringToOString(xType->getStringValue(), RTL_TEXTENCODING_UTF8); } return std::make_shared(val, lang, type); } // extract blank or URI or literal node - call without Mutex locked void librdf_TypeConverter::extractNodeToCacheKey_NoLock( 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_NoLock(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 " + xLiteral->getValue() + "\t" + xLiteral->getLanguage()); const uno::Reference< rdf::XURI > xType(xLiteral->getDatatype()); if (xType.is()) rBuffer.append("\t" + xType->getStringValue()); } // create blank or URI or literal node librdf_node* librdf_TypeConverter::mkNode_Lock( librdf_world* i_pWorld, Node const*const i_pNode) { if (!i_pNode) return nullptr; Resource const*const pResource(dynamic_cast(i_pNode)); if (pResource) { return mkResource_Lock(i_pWorld, pResource); } Literal const*const pLiteral(dynamic_cast(i_pNode)); assert(pLiteral); librdf_node * ret(nullptr); if (pLiteral->language.isEmpty()) { if (!pLiteral->type) { ret = librdf_new_node_from_literal(i_pWorld, reinterpret_cast(pLiteral->value.getStr()) , nullptr, 0); } else { const std::shared_ptr pDatatype( mkURI_Lock(i_pWorld, *pLiteral->type), safe_librdf_free_uri); ret = librdf_new_node_from_typed_literal(i_pWorld, reinterpret_cast(pLiteral->value.getStr()) , nullptr, pDatatype.get()); } } else { if (!pLiteral->type) { ret = librdf_new_node_from_literal(i_pWorld, reinterpret_cast(pLiteral->value.getStr()) , pLiteral->language.getStr(), 0); } else { OSL_FAIL("mkNode: invalid literal"); return nullptr; } } if (!ret) { throw uno::RuntimeException( "librdf_TypeConverter::mkNode: librdf_new_node_from_literal failed", nullptr); } return ret; } // extract statement - call without Mutex locked librdf_TypeConverter::Statement librdf_TypeConverter::extractStatement_NoLock( const uno::Reference< rdf::XResource > & i_xSubject, const uno::Reference< rdf::XURI > & i_xPredicate, const uno::Reference< rdf::XNode > & i_xObject) { std::shared_ptr const pSubject( extractResource_NoLock(i_xSubject)); std::shared_ptr const pPredicate( std::dynamic_pointer_cast(extractResource_NoLock(i_xPredicate))); std::shared_ptr const pObject(extractNode_NoLock(i_xObject)); return Statement(pSubject, pPredicate, pObject); } librdf_statement* librdf_TypeConverter::mkStatement_Lock(librdf_world* i_pWorld, Statement const& i_rStatement) { librdf_node *const pSubject( mkResource_Lock(i_pWorld, i_rStatement.pSubject.get()) ); librdf_node* pPredicate(nullptr); librdf_node* pObject(nullptr); try { pPredicate = mkResource_Lock(i_pWorld, i_rStatement.pPredicate.get()); try { pObject = mkNode_Lock(i_pWorld, i_rStatement.pObject.get()); } catch (...) { safe_librdf_free_node(pPredicate); throw; } } catch (...) { safe_librdf_free_node(pSubject); throw; } // NB: this takes ownership of the nodes! (which is really ugly) librdf_statement* pStatement( librdf_new_statement_from_nodes(i_pWorld, pSubject, pPredicate, pObject) ); if (!pStatement) { throw uno::RuntimeException( "librdf_TypeConverter::mkStatement: " "librdf_new_statement_from_nodes failed", nullptr); } return pStatement; } uno::Reference librdf_TypeConverter::convertToXURI(librdf_uri* i_pURI) const { if (!i_pURI) return nullptr; const unsigned char* uri( librdf_uri_as_string(i_pURI) ); if (!uri) { throw uno::RuntimeException( "librdf_TypeConverter::convertToXURI: " "librdf_uri_as_string failed", m_rRep); } OUString uriU( OStringToOUString( std::string_view(reinterpret_cast(uri)), RTL_TEXTENCODING_UTF8) ); try { return rdf::URI::create(m_xContext, uriU); } catch (const lang::IllegalArgumentException &) { css::uno::Any anyEx = cppu::getCaughtException(); throw lang::WrappedTargetRuntimeException( "librdf_TypeConverter::convertToXURI: " "illegal uri", m_rRep, anyEx); } } uno::Reference librdf_TypeConverter::convertToXURI(librdf_node* i_pNode) const { if (!i_pNode) return nullptr; if (librdf_node_is_resource(i_pNode)) { librdf_uri* pURI( librdf_node_get_uri(i_pNode) ); if (!pURI) { throw uno::RuntimeException( "librdf_TypeConverter::convertToXURI: " "resource has no uri", m_rRep); } return convertToXURI(pURI); } else { OSL_FAIL("convertToXURI: unknown librdf_node"); return nullptr; } } uno::Reference librdf_TypeConverter::convertToXResource(librdf_node* i_pNode) const { if (!i_pNode) return nullptr; if (librdf_node_is_blank(i_pNode)) { const unsigned char* label( librdf_node_get_blank_identifier(i_pNode) ); if (!label) { throw uno::RuntimeException( "librdf_TypeConverter::convertToXResource: " "blank node has no label", m_rRep); } OUString labelU( OStringToOUString( std::string_view(reinterpret_cast(label)), RTL_TEXTENCODING_UTF8) ); try { return rdf::BlankNode::create(m_xContext, labelU); } catch (const lang::IllegalArgumentException &) { css::uno::Any anyEx = cppu::getCaughtException(); throw lang::WrappedTargetRuntimeException( "librdf_TypeConverter::convertToXResource: " "illegal blank node label", m_rRep, anyEx); } } else { return convertToXURI(i_pNode); } } uno::Reference librdf_TypeConverter::convertToXNode(librdf_node* i_pNode) const { if (!i_pNode) return nullptr; if (!librdf_node_is_literal(i_pNode)) { return convertToXResource(i_pNode); } const unsigned char* value( librdf_node_get_literal_value(i_pNode) ); if (!value) { throw uno::RuntimeException( "librdf_TypeConverter::convertToXNode: " "literal has no value", m_rRep); } const char * lang( librdf_node_get_literal_value_language(i_pNode) ); librdf_uri* pType( librdf_node_get_literal_value_datatype_uri(i_pNode) ); OSL_ENSURE(!lang || !pType, "convertToXNode: invalid literal"); const OUString valueU( OStringToOUString( std::string_view(reinterpret_cast(value)), RTL_TEXTENCODING_UTF8) ); if (lang) { const OUString langU( OStringToOUString( std::string_view(lang), RTL_TEXTENCODING_UTF8) ); return rdf::Literal::createWithLanguage(m_xContext, valueU, langU); } else if (pType) { uno::Reference xType(convertToXURI(pType)); OSL_ENSURE(xType.is(), "convertToXNode: null uri"); return rdf::Literal::createWithType(m_xContext, valueU, xType); } else { return rdf::Literal::create(m_xContext, valueU); } } rdf::Statement librdf_TypeConverter::convertToStatement(librdf_statement* i_pStmt, librdf_node* i_pContext) const { if (!i_pStmt) { throw uno::RuntimeException(); } return rdf::Statement( convertToXResource(librdf_statement_get_subject(i_pStmt)), convertToXURI(librdf_statement_get_predicate(i_pStmt)), convertToXNode(librdf_statement_get_object(i_pStmt)), convertToXURI(i_pContext)); } } // closing anonymous implementation namespace extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* unoxml_rdfRepository_get_implementation( css::uno::XComponentContext* context , css::uno::Sequence const&) { return cppu::acquire(new librdf_Repository(context)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */