1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <string.h>
21 
22 #include <map>
23 #include <memory>
24 #include <set>
25 #include <string_view>
26 #include <iterator>
27 #include <algorithm>
28 #include <atomic>
29 
30 #include <optional>
31 
32 #include <libxslt/security.h>
33 
34 #include <redland.h>
35 
36 #include <com/sun/star/container/ElementExistException.hpp>
37 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
38 #include <com/sun/star/lang/XServiceInfo.hpp>
39 #include <com/sun/star/lang/XInitialization.hpp>
40 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
41 #include <com/sun/star/lang/IllegalArgumentException.hpp>
42 #include <com/sun/star/io/XSeekable.hpp>
43 #include <com/sun/star/text/XTextRange.hpp>
44 #include <com/sun/star/rdf/ParseException.hpp>
45 #include <com/sun/star/rdf/QueryException.hpp>
46 #include <com/sun/star/rdf/RepositoryException.hpp>
47 #include <com/sun/star/rdf/XDocumentRepository.hpp>
48 #include <com/sun/star/rdf/XLiteral.hpp>
49 #include <com/sun/star/rdf/FileFormat.hpp>
50 #include <com/sun/star/rdf/BlankNode.hpp>
51 #include <com/sun/star/rdf/URI.hpp>
52 #include <com/sun/star/rdf/Literal.hpp>
53 
54 #include <rtl/ref.hxx>
55 #include <rtl/strbuf.hxx>
56 #include <rtl/ustrbuf.hxx>
57 #include <rtl/ustring.hxx>
58 #include <osl/diagnose.h>
59 #include <cppuhelper/exc_hlp.hxx>
60 #include <cppuhelper/implbase.hxx>
61 #include <cppuhelper/supportsservice.hxx>
62 #include <cppuhelper/weakref.hxx>
63 
64 #include <comphelper/sequence.hxx>
65 #include <comphelper/xmltools.hxx>
66 
67 #include <com/sun/star/embed/XEncryptionProtectedSource2.hpp>
68 
69 /**
70     Implementation of the service com.sun.star.rdf.Repository.
71 
72     This implementation uses the Redland RDF library (librdf).
73 
74     There are several classes involved:
75     librdf_TypeConverter:   helper class to convert data types redland <-> uno
76     librdf_Repository:      the main repository, does almost all the work
77     librdf_NamedGraph:      the XNamedGraph, forwards everything to repository
78     librdf_GraphResult:     an XEnumeration<Statement>
79     librdf_QuerySelectResult:   an XEnumeration<sequence<XNode>>
80 
81  */
82 
83 /// anonymous implementation namespace
84 namespace {
85 
86 class librdf_NamedGraph;
87 class librdf_Repository;
88 
89 using namespace ::com::sun::star;
90 
91 typedef std::map< OUString, ::rtl::Reference<librdf_NamedGraph> >
92     NamedGraphMap_t;
93 
94 const char s_sparql [] = "sparql";
95 const char s_nsOOo  [] = "http://openoffice.org/2004/office/rdfa/";
96 
97 
98 //FIXME: this approach is not ideal. can we use blank nodes instead?
isInternalContext(librdf_node * i_pNode)99 bool isInternalContext(librdf_node *i_pNode) noexcept
100 {
101     OSL_ENSURE(i_pNode, "isInternalContext: context null");
102     OSL_ENSURE(librdf_node_is_resource(i_pNode),
103         "isInternalContext: context not resource");
104     if (i_pNode) {
105         librdf_uri *pURI(librdf_node_get_uri(i_pNode));
106         OSL_ENSURE(pURI, "isInternalContext: URI null");
107         if (pURI) {
108             unsigned char *pContextURI(librdf_uri_as_string(pURI));
109             assert(pContextURI && "isInternalContext: URI string null");
110             // if prefix matches reserved uri, it is RDFa context
111             if (!strncmp(reinterpret_cast<char *>(pContextURI),
112                     s_nsOOo, sizeof(s_nsOOo)-1)) {
113                 return true;
114             }
115         }
116         return false;
117     }
118     return true;
119 }
120 
121 
122 // n.b.: librdf destructor functions dereference null pointers!
123 //       so they need to be wrapped to be usable with std::shared_ptr.
safe_librdf_free_world(librdf_world * const world)124 void safe_librdf_free_world(librdf_world *const world)
125 {
126     if (world) { librdf_free_world(world); }
127 }
safe_librdf_free_model(librdf_model * const model)128 void safe_librdf_free_model(librdf_model *const model)
129 {
130     if (model) { librdf_free_model(model); }
131 }
safe_librdf_free_node(librdf_node * node)132 void safe_librdf_free_node(librdf_node* node)
133 {
134     if (node) { librdf_free_node(node); }
135 }
safe_librdf_free_parser(librdf_parser * const parser)136 void safe_librdf_free_parser(librdf_parser *const parser)
137 {
138     if (parser) { librdf_free_parser(parser); }
139 }
safe_librdf_free_query(librdf_query * const query)140 void safe_librdf_free_query(librdf_query *const query)
141 {
142     if (query) { librdf_free_query(query); }
143 }
144 void
safe_librdf_free_query_results(librdf_query_results * const query_results)145 safe_librdf_free_query_results(librdf_query_results *const query_results)
146 {
147     if (query_results) { librdf_free_query_results(query_results); }
148 }
safe_librdf_free_serializer(librdf_serializer * const serializer)149 void safe_librdf_free_serializer(librdf_serializer *const serializer)
150 {
151     if (serializer) { librdf_free_serializer(serializer); }
152 }
safe_librdf_free_statement(librdf_statement * const statement)153 void safe_librdf_free_statement(librdf_statement *const statement)
154 {
155     if (statement) { librdf_free_statement(statement); }
156 }
safe_librdf_free_storage(librdf_storage * const storage)157 void safe_librdf_free_storage(librdf_storage *const storage)
158 {
159     if (storage) { librdf_free_storage(storage); }
160 }
safe_librdf_free_stream(librdf_stream * const stream)161 void safe_librdf_free_stream(librdf_stream *const stream)
162 {
163     if (stream) { librdf_free_stream(stream); }
164 }
safe_librdf_free_uri(librdf_uri * const uri)165 void safe_librdf_free_uri(librdf_uri *const uri)
166 {
167     if (uri) { librdf_free_uri(uri); }
168 }
169 
170 
171 /** converts between librdf types and UNO API types.
172  */
173 class librdf_TypeConverter
174 {
175 public:
176 
177     // some wrapper classes to temporarily hold values of UNO XNodes
178     struct Node
179     {
~Node__anon59ac1e5d0111::librdf_TypeConverter::Node180         virtual ~Node() {}
181     };
182     struct Resource : public Node { };
183     struct URI : public Resource
184     {
185         OString const value;
URI__anon59ac1e5d0111::librdf_TypeConverter::URI186         explicit URI(OString const& i_rValue)
187             : value(i_rValue)
188         { }
189     };
190     struct BlankNode : public Resource
191     {
192         OString const value;
BlankNode__anon59ac1e5d0111::librdf_TypeConverter::BlankNode193         explicit BlankNode(OString const& i_rValue)
194             : value(i_rValue)
195         { }
196     };
197     struct Literal : public Node
198     {
199         OString const value;
200         OString const language;
201         ::std::optional<OString> const type;
Literal__anon59ac1e5d0111::librdf_TypeConverter::Literal202         Literal(OString const& i_rValue, OString const& i_rLanguage,
203                 ::std::optional<OString> const& i_rType)
204             : value(i_rValue)
205             , language(i_rLanguage)
206             , type(i_rType)
207         { }
208     };
209     struct Statement
210     {
211         std::shared_ptr<Resource> const pSubject;
212         std::shared_ptr<URI> const pPredicate;
213         std::shared_ptr<Node> const pObject;
Statement__anon59ac1e5d0111::librdf_TypeConverter::Statement214         Statement(std::shared_ptr<Resource> const& i_pSubject,
215                   std::shared_ptr<URI> const& i_pPredicate,
216                   std::shared_ptr<Node> const& i_pObject)
217             : pSubject(i_pSubject)
218             , pPredicate(i_pPredicate)
219             , pObject(i_pObject)
220         { }
221     };
222 
librdf_TypeConverter(uno::Reference<uno::XComponentContext> const & i_xContext,librdf_Repository & i_rRep)223     librdf_TypeConverter(
224             uno::Reference< uno::XComponentContext > const & i_xContext,
225             librdf_Repository &i_rRep)
226         : m_xContext(i_xContext)
227         , m_rRep(i_rRep)
228     { };
229 
230     librdf_world *createWorld_Lock() const;
231     librdf_storage *createStorage_Lock(librdf_world *i_pWorld) const;
232     librdf_model *createModel_Lock(librdf_world *i_pWorld,
233         librdf_storage * i_pStorage) const;
234     static librdf_uri* mkURI_Lock(librdf_world* i_pWorld,
235         const OString & i_rURI);
236     static librdf_node* mkResource_Lock(librdf_world* i_pWorld,
237         const Resource * i_pResource);
238     static librdf_node* mkNode_Lock(librdf_world* i_pWorld,
239         const Node * i_pNode);
240     static librdf_statement* mkStatement_Lock(librdf_world* i_pWorld,
241         Statement const& i_rStatement);
242     static std::shared_ptr<Resource> extractResource_NoLock(
243         const uno::Reference< rdf::XResource > & i_xResource);
244     static void extractResourceToCacheKey_NoLock(
245         const uno::Reference< rdf::XResource > & i_xResource,
246         OUStringBuffer& rBuf);
247     static std::shared_ptr<Node> extractNode_NoLock(
248         const uno::Reference< rdf::XNode > & i_xNode);
249     static void extractNodeToCacheKey_NoLock(
250         const uno::Reference< rdf::XNode > & i_xNode,
251         OUStringBuffer& rBuffer);
252     static Statement extractStatement_NoLock(
253         const uno::Reference< rdf::XResource > & i_xSubject,
254         const uno::Reference< rdf::XURI > & i_xPredicate,
255         const uno::Reference< rdf::XNode > & i_xObject);
256     uno::Reference<rdf::XURI> convertToXURI(librdf_uri* i_pURI) const;
257     uno::Reference<rdf::XURI> convertToXURI(librdf_node* i_pURI) const;
258     uno::Reference<rdf::XResource>
259         convertToXResource(librdf_node* i_pNode) const;
260     uno::Reference<rdf::XNode> convertToXNode(librdf_node* i_pNode) const;
261     rdf::Statement
262         convertToStatement(librdf_statement* i_pStmt, librdf_node* i_pContext)
263         const;
264 
265 private:
266     uno::Reference< uno::XComponentContext > const m_xContext;
267     librdf_Repository & m_rRep;
268 };
269 
270 
271 /** implements the repository service.
272  */
273 class librdf_Repository:
274 //    private ::cppu::BaseMutex,
275     public ::cppu::WeakImplHelper<
276         lang::XServiceInfo,
277         rdf::XDocumentRepository,
278         lang::XInitialization>
279 {
280 public:
281 
282     explicit librdf_Repository(
283         uno::Reference< uno::XComponentContext > const & i_xContext);
284     virtual ~librdf_Repository() override;
285 
286     // css::lang::XServiceInfo:
287     virtual OUString SAL_CALL getImplementationName() override;
288     virtual sal_Bool SAL_CALL supportsService(
289             const OUString & ServiceName) override;
290     virtual uno::Sequence< OUString > SAL_CALL
291         getSupportedServiceNames() override;
292 
293     // css::rdf::XRepository:
294     virtual uno::Reference< rdf::XBlankNode > SAL_CALL createBlankNode() override;
295     virtual uno::Reference<rdf::XNamedGraph> SAL_CALL importGraph(
296             ::sal_Int16 i_Format,
297             const uno::Reference< io::XInputStream > & i_xInStream,
298             const uno::Reference< rdf::XURI > & i_xGraphName,
299             const uno::Reference< rdf::XURI > & i_xBaseURI) override;
300     virtual void SAL_CALL exportGraph(::sal_Int16 i_Format,
301             const uno::Reference< io::XOutputStream > & i_xOutStream,
302             const uno::Reference< rdf::XURI > & i_xGraphName,
303             const uno::Reference< rdf::XURI > & i_xBaseURI) override;
304     virtual uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL
305         getGraphNames() override;
306     virtual uno::Reference< rdf::XNamedGraph > SAL_CALL getGraph(
307             const uno::Reference< rdf::XURI > & i_xGraphName) override;
308     virtual uno::Reference< rdf::XNamedGraph > SAL_CALL createGraph(
309             const uno::Reference< rdf::XURI > & i_xGraphName) override;
310     virtual void SAL_CALL destroyGraph(
311             const uno::Reference< rdf::XURI > & i_xGraphName) override;
312     virtual uno::Reference< container::XEnumeration > SAL_CALL getStatements(
313             const uno::Reference< rdf::XResource > & i_xSubject,
314             const uno::Reference< rdf::XURI > & i_xPredicate,
315             const uno::Reference< rdf::XNode > & i_xObject) override;
316     virtual uno::Reference< rdf::XQuerySelectResult > SAL_CALL
317             querySelect(const OUString & i_rQuery) override;
318     virtual uno::Reference< container::XEnumeration > SAL_CALL
319         queryConstruct(const OUString & i_rQuery) override;
320     virtual sal_Bool SAL_CALL queryAsk(const OUString & i_rQuery) override;
321 
322     // css::rdf::XDocumentRepository:
323     virtual void SAL_CALL setStatementRDFa(
324             const uno::Reference< rdf::XResource > & i_xSubject,
325             const uno::Sequence< uno::Reference< rdf::XURI > > & i_rPredicates,
326             const uno::Reference< rdf::XMetadatable > & i_xObject,
327             const OUString & i_rRDFaContent,
328             const uno::Reference< rdf::XURI > & i_xRDFaDatatype) override;
329     virtual void SAL_CALL removeStatementRDFa(
330             const uno::Reference< rdf::XMetadatable > & i_xElement) override;
331     virtual beans::Pair< uno::Sequence<rdf::Statement>, sal_Bool > SAL_CALL
332         getStatementRDFa(uno::Reference< rdf::XMetadatable > const& i_xElement) override;
333     virtual uno::Reference< container::XEnumeration > SAL_CALL
334         getStatementsRDFa(
335             const uno::Reference< rdf::XResource > & i_xSubject,
336             const uno::Reference< rdf::XURI > & i_xPredicate,
337             const uno::Reference< rdf::XNode > & i_xObject) override;
338 
339     // css::lang::XInitialization:
340     virtual void SAL_CALL initialize(
341             const uno::Sequence< css::uno::Any > & i_rArguments) override;
342 
343     // XNamedGraph forwards ---------------------------------------------
344     NamedGraphMap_t::iterator clearGraph_NoLock(
345             const OUString & i_rGraphName,
346             bool i_Internal = false );
347     NamedGraphMap_t::iterator clearGraph_Lock(
348             const OUString & i_rGraphName,
349             bool i_Internal);
350     void addStatementGraph_NoLock(
351             const uno::Reference< rdf::XResource > & i_xSubject,
352             const uno::Reference< rdf::XURI > & i_xPredicate,
353             const uno::Reference< rdf::XNode > & i_xObject,
354             const uno::Reference< rdf::XURI > & i_xName );
355 //        throw (uno::RuntimeException, lang::IllegalArgumentException,
356 //            container::NoSuchElementException, rdf::RepositoryException);
357     void addStatementGraph_Lock(
358         librdf_TypeConverter::Statement const& i_rStatement,
359         OUString const& i_rGraphName,
360         bool i_Internal);
361     void removeStatementsGraph_NoLock(
362             const uno::Reference< rdf::XResource > & i_xSubject,
363             const uno::Reference< rdf::XURI > & i_xPredicate,
364             const uno::Reference< rdf::XNode > & i_xObject,
365             const uno::Reference< rdf::XURI > & i_xName );
366 //        throw (uno::RuntimeException, lang::IllegalArgumentException,
367 //            container::NoSuchElementException, rdf::RepositoryException);
368     std::vector<rdf::Statement> getStatementsGraph_NoLock(
369             const uno::Reference< rdf::XResource > & i_xSubject,
370             const uno::Reference< rdf::XURI > & i_xPredicate,
371             const uno::Reference< rdf::XNode > & i_xObject,
372             const uno::Reference< rdf::XURI > & i_xName,
373             bool i_Internal = false );
374 //        throw (uno::RuntimeException, lang::IllegalArgumentException,
375 //            container::NoSuchElementException, rdf::RepositoryException);
376 
getTypeConverter() const377     const librdf_TypeConverter& getTypeConverter() const { return m_TypeConverter; };
378 
379 private:
380 
381     librdf_Repository(librdf_Repository const&) = delete;
382     librdf_Repository& operator=(librdf_Repository const&) = delete;
383 
384     /// this is const, no need to lock m_aMutex to access it
385     uno::Reference< uno::XComponentContext > const m_xContext;
386 
387     /// librdf global data
388     /** N.B.: The redland documentation gives the impression that you can have
389               as many librdf_worlds as you like. This is true in the same sense
390               that you can physically be in as many places as you like.
391               Well, you can, just not at the same time.
392               The ugly truth is that destroying a librdf_world kills a bunch
393               of static variables; other librdf_worlds become very unhappy
394               when they access these.
395               And of course this is not documented anywhere that I could find.
396               So we allocate a single world, and refcount that.
397      */
398     static std::shared_ptr<librdf_world> m_pWorld;
399     /// refcount
400     static sal_uInt32 m_NumInstances;
401     /// mutex for m_pWorld - redland is not as threadsafe as is often claimed
402     static osl::Mutex m_aMutex;
403 
404     // NB: sequence of the shared pointers is important!
405     /// librdf repository storage
406     std::shared_ptr<librdf_storage> m_pStorage;
407     /// librdf repository model
408     std::shared_ptr<librdf_model> m_pModel;
409 
410     /// all named graphs
411     NamedGraphMap_t m_NamedGraphs;
412 
413     /// type conversion helper - stateless
414     librdf_TypeConverter m_TypeConverter;
415 
416     /// set of xml:ids of elements with xhtml:content
417     ::std::set< OUString > m_RDFaXHTMLContentSet;
418 };
419 
420 
421 /** result of operations that return a graph, i.e.,
422     an XEnumeration of statements.
423  */
424 class librdf_GraphResult:
425     public ::cppu::WeakImplHelper<
426         container::XEnumeration>
427 {
428 public:
429 
librdf_GraphResult(librdf_Repository * i_pRepository,::osl::Mutex & i_rMutex,std::shared_ptr<librdf_stream> const & i_pStream,std::shared_ptr<librdf_node> const & i_pContext,std::shared_ptr<librdf_query> const & i_pQuery=std::shared_ptr<librdf_query> ())430     librdf_GraphResult(librdf_Repository *i_pRepository,
431             ::osl::Mutex & i_rMutex,
432             std::shared_ptr<librdf_stream> const& i_pStream,
433             std::shared_ptr<librdf_node> const& i_pContext,
434             std::shared_ptr<librdf_query>  const& i_pQuery =
435                 std::shared_ptr<librdf_query>() )
436         : m_xRep(i_pRepository)
437         , m_rMutex(i_rMutex)
438         , m_pQuery(i_pQuery)
439         , m_pContext(i_pContext)
440         , m_pStream(i_pStream)
441     { };
442 
~librdf_GraphResult()443     virtual ~librdf_GraphResult() override
444     {
445         ::osl::MutexGuard g(m_rMutex); // lock mutex when destroying members
446         const_cast<std::shared_ptr<librdf_stream>& >(m_pStream).reset();
447         const_cast<std::shared_ptr<librdf_node>& >(m_pContext).reset();
448         const_cast<std::shared_ptr<librdf_query>& >(m_pQuery).reset();
449     }
450 
451     // css::container::XEnumeration:
452     virtual sal_Bool SAL_CALL hasMoreElements() override;
453     virtual uno::Any SAL_CALL nextElement() override;
454 
455 private:
456 
457     librdf_GraphResult(librdf_GraphResult const&) = delete;
458     librdf_GraphResult& operator=(librdf_GraphResult const&) = delete;
459 
460     // NB: this is not a weak pointer: streams _must_ be deleted before the
461     //     storage they point into, so we keep the repository alive here
462     // also, sequence is important: the stream must be destroyed first.
463     ::rtl::Reference< librdf_Repository > m_xRep;
464     // needed for synchronizing access to librdf (it doesn't do win32 threading)
465     ::osl::Mutex & m_rMutex;
466     // the query (in case this is a result of a graph query)
467     // not that the redland documentation spells this out explicitly, but
468     // queries must be freed only after all the results are completely read
469     std::shared_ptr<librdf_query>  const m_pQuery;
470     std::shared_ptr<librdf_node>   const m_pContext;
471     std::shared_ptr<librdf_stream> const m_pStream;
472 
473     librdf_node* getContext_Lock() const;
474 };
475 
476 
477 // css::container::XEnumeration:
478 sal_Bool SAL_CALL
hasMoreElements()479 librdf_GraphResult::hasMoreElements()
480 {
481     ::osl::MutexGuard g(m_rMutex);
482     return m_pStream && !librdf_stream_end(m_pStream.get());
483 }
484 
getContext_Lock() const485 librdf_node* librdf_GraphResult::getContext_Lock() const
486 {
487     if (!m_pStream || librdf_stream_end(m_pStream.get()))
488         return nullptr;
489     librdf_node *pCtxt(
490 #if LIBRDF_VERSION >= 10012
491         librdf_stream_get_context2(m_pStream.get()) );
492 #else
493         static_cast<librdf_node *>(librdf_stream_get_context(m_pStream.get())) );
494 #endif
495     if (pCtxt)
496         return pCtxt;
497     return m_pContext.get();
498 }
499 
500 css::uno::Any SAL_CALL
nextElement()501 librdf_GraphResult::nextElement()
502 {
503     ::osl::MutexGuard g(m_rMutex);
504     if (m_pStream && librdf_stream_end(m_pStream.get())) {
505         throw container::NoSuchElementException();
506     }
507     librdf_node * pCtxt = getContext_Lock();
508 
509     librdf_statement *pStmt( librdf_stream_get_object(m_pStream.get()) );
510     if (!pStmt) {
511         rdf::QueryException e(
512             "librdf_GraphResult::nextElement: "
513             "librdf_stream_get_object failed", *this);
514         throw lang::WrappedTargetException(
515             "librdf_GraphResult::nextElement: "
516             "librdf_stream_get_object failed", *this,
517                 uno::makeAny(e));
518     }
519     // NB: pCtxt may be null here if this is result of a graph query
520     if (pCtxt && isInternalContext(pCtxt)) {
521         pCtxt = nullptr; // XML ID context is implementation detail!
522     }
523     rdf::Statement Stmt(
524         m_xRep->getTypeConverter().convertToStatement(pStmt, pCtxt) );
525     // NB: this will invalidate current item.
526     librdf_stream_next(m_pStream.get());
527     return uno::makeAny(Stmt);
528 }
529 
530 
531 /** result of operations that return a graph, i.e.,
532     an XEnumeration of statements.
533  */
534 class librdf_GraphResult2:
535     public ::cppu::WeakImplHelper<
536         container::XEnumeration>
537 {
538 public:
539 
librdf_GraphResult2(std::vector<rdf::Statement> statements)540     librdf_GraphResult2(std::vector<rdf::Statement> statements)
541         : m_vStatements(std::move(statements))
542     { };
543 
544     // css::container::XEnumeration:
545     virtual sal_Bool SAL_CALL hasMoreElements() override;
546     virtual uno::Any SAL_CALL nextElement() override;
547 
548 private:
549 
550     std::vector<rdf::Statement> m_vStatements;
551     std::atomic<std::size_t> m_nIndex = 0;
552 };
553 
554 
555 // css::container::XEnumeration:
556 sal_Bool SAL_CALL
hasMoreElements()557 librdf_GraphResult2::hasMoreElements()
558 {
559     return m_nIndex < m_vStatements.size();
560 }
561 
562 css::uno::Any SAL_CALL
nextElement()563 librdf_GraphResult2::nextElement()
564 {
565     std::size_t const n = m_nIndex++;
566     if (m_vStatements.size() <= n)
567     {
568         m_nIndex = m_vStatements.size(); // avoid overflow
569         throw container::NoSuchElementException();
570     }
571     return uno::makeAny(m_vStatements[n]);
572 }
573 
574 /** result of tuple queries ("SELECT").
575  */
576 class librdf_QuerySelectResult:
577     public ::cppu::WeakImplHelper<
578         rdf::XQuerySelectResult>
579 {
580 public:
581 
librdf_QuerySelectResult(librdf_Repository * i_pRepository,::osl::Mutex & i_rMutex,std::shared_ptr<librdf_query> const & i_pQuery,std::shared_ptr<librdf_query_results> const & i_pQueryResult,uno::Sequence<OUString> const & i_rBindingNames)582     librdf_QuerySelectResult(librdf_Repository *i_pRepository,
583             ::osl::Mutex & i_rMutex,
584             std::shared_ptr<librdf_query>  const& i_pQuery,
585             std::shared_ptr<librdf_query_results> const& i_pQueryResult,
586             uno::Sequence< OUString > const& i_rBindingNames )
587         : m_xRep(i_pRepository)
588         , m_rMutex(i_rMutex)
589         , m_pQuery(i_pQuery)
590         , m_pQueryResult(i_pQueryResult)
591         , m_BindingNames(i_rBindingNames)
592     { };
593 
~librdf_QuerySelectResult()594     virtual ~librdf_QuerySelectResult() override
595     {
596         ::osl::MutexGuard g(m_rMutex); // lock mutex when destroying members
597         const_cast<std::shared_ptr<librdf_query_results>& >(m_pQueryResult)
598             .reset();
599         const_cast<std::shared_ptr<librdf_query>& >(m_pQuery).reset();
600     }
601 
602     // css::container::XEnumeration:
603     virtual sal_Bool SAL_CALL hasMoreElements() override;
604     virtual uno::Any SAL_CALL nextElement() override;
605 
606     // css::rdf::XQuerySelectResult:
607     virtual uno::Sequence< OUString > SAL_CALL getBindingNames() override;
608 
609 private:
610 
611     librdf_QuerySelectResult(librdf_QuerySelectResult const&) = delete;
612     librdf_QuerySelectResult& operator=(librdf_QuerySelectResult const&) = delete;
613 
614     // NB: this is not a weak pointer: streams _must_ be deleted before the
615     //     storage they point into, so we keep the repository alive here
616     // also, sequence is important: the stream must be destroyed first.
617     ::rtl::Reference< librdf_Repository > m_xRep;
618     // needed for synchronizing access to librdf (it doesn't do win32 threading)
619     ::osl::Mutex & m_rMutex;
620     // not that the redland documentation spells this out explicitly, but
621     // queries must be freed only after all the results are completely read
622     std::shared_ptr<librdf_query> const m_pQuery;
623     std::shared_ptr<librdf_query_results> const m_pQueryResult;
624     uno::Sequence< OUString > const m_BindingNames;
625 };
626 
627 
628 // css::container::XEnumeration:
629 sal_Bool SAL_CALL
hasMoreElements()630 librdf_QuerySelectResult::hasMoreElements()
631 {
632     ::osl::MutexGuard g(m_rMutex);
633     return !librdf_query_results_finished(m_pQueryResult.get());
634 }
635 
636 class NodeArray : private std::vector<librdf_node*>
637 {
638 public:
NodeArray(int cnt)639     NodeArray(int cnt) : std::vector<librdf_node*>(cnt) {}
640 
~NodeArray()641     ~NodeArray() noexcept
642     {
643         std::for_each(begin(), end(), safe_librdf_free_node);
644     }
645 
646     using std::vector<librdf_node*>::data;
647     using std::vector<librdf_node*>::operator[];
648 };
649 
650 css::uno::Any SAL_CALL
nextElement()651 librdf_QuerySelectResult::nextElement()
652 {
653     ::osl::MutexGuard g(m_rMutex);
654     if (librdf_query_results_finished(m_pQueryResult.get())) {
655         throw container::NoSuchElementException();
656     }
657     sal_Int32 count(m_BindingNames.getLength());
658     OSL_ENSURE(count >= 0, "negative length?");
659     NodeArray aNodes(count);
660     if (librdf_query_results_get_bindings(m_pQueryResult.get(), nullptr,
661                 aNodes.data()))
662     {
663         rdf::QueryException e(
664             "librdf_QuerySelectResult::nextElement: "
665             "librdf_query_results_get_bindings failed", *this);
666         throw lang::WrappedTargetException(
667             "librdf_QuerySelectResult::nextElement: "
668             "librdf_query_results_get_bindings failed", *this,
669             uno::makeAny(e));
670     }
671     uno::Sequence< uno::Reference< rdf::XNode > > ret(count);
672     for (int i = 0; i < count; ++i) {
673         ret[i] = m_xRep->getTypeConverter().convertToXNode(aNodes[i]);
674     }
675     // NB: this will invalidate current item.
676     librdf_query_results_next(m_pQueryResult.get());
677     return uno::makeAny(ret);
678 }
679 
680 // css::rdf::XQuerySelectResult:
681 uno::Sequence< OUString > SAL_CALL
getBindingNames()682 librdf_QuerySelectResult::getBindingNames()
683 {
684     // const - no lock needed
685     return m_BindingNames;
686 }
687 
688 
689 /** represents a named graph, and forwards all the work to repository.
690  */
691 class librdf_NamedGraph:
692     public ::cppu::WeakImplHelper<
693         rdf::XNamedGraph>
694 {
695 public:
librdf_NamedGraph(librdf_Repository * i_pRep,uno::Reference<rdf::XURI> const & i_xName)696     librdf_NamedGraph(librdf_Repository * i_pRep,
697             uno::Reference<rdf::XURI> const & i_xName)
698         : m_wRep(i_pRep)
699         , m_pRep(i_pRep)
700         , m_xName(i_xName)
701     { };
702 
703     // css::rdf::XNode:
704     virtual OUString SAL_CALL getStringValue() override;
705 
706     // css::rdf::XURI:
707     virtual OUString SAL_CALL getNamespace() override;
708     virtual OUString SAL_CALL getLocalName() override;
709 
710     // css::rdf::XNamedGraph:
711     virtual uno::Reference<rdf::XURI> SAL_CALL getName() override;
712     virtual void SAL_CALL clear() override;
713     virtual void SAL_CALL addStatement(
714             const uno::Reference< rdf::XResource > & i_xSubject,
715             const uno::Reference< rdf::XURI > & i_xPredicate,
716             const uno::Reference< rdf::XNode > & i_xObject) override;
717     virtual void SAL_CALL removeStatements(
718             const uno::Reference< rdf::XResource > & i_xSubject,
719             const uno::Reference< rdf::XURI > & i_xPredicate,
720             const uno::Reference< rdf::XNode > & i_xObject) override;
721     virtual uno::Reference< container::XEnumeration > SAL_CALL getStatements(
722             const uno::Reference< rdf::XResource > & i_xSubject,
723             const uno::Reference< rdf::XURI > & i_xPredicate,
724             const uno::Reference< rdf::XNode > & i_xObject) override;
725 
726 private:
727 
728     librdf_NamedGraph(librdf_NamedGraph const&) = delete;
729     librdf_NamedGraph& operator=(librdf_NamedGraph const&) = delete;
730 
731     static OUString createCacheKey_NoLock(
732         const uno::Reference< rdf::XResource > & i_xSubject,
733         const uno::Reference< rdf::XURI > & i_xPredicate,
734         const uno::Reference< rdf::XNode > & i_xObject);
735 
736     /// weak reference: this is needed to check if m_pRep is valid
737     uno::WeakReference< rdf::XRepository > const m_wRep;
738     librdf_Repository *const m_pRep;
739     uno::Reference< rdf::XURI > const m_xName;
740 
741     /// Querying is rather slow, so cache the results.
742     std::map<OUString, std::vector<rdf::Statement>> m_aStatementsCache;
743     ::osl::Mutex m_CacheMutex;
744 };
745 
746 
747 // css::rdf::XNode:
getStringValue()748 OUString SAL_CALL librdf_NamedGraph::getStringValue()
749 {
750     return m_xName->getStringValue();
751 }
752 
753 // css::rdf::XURI:
getNamespace()754 OUString SAL_CALL librdf_NamedGraph::getNamespace()
755 {
756     return m_xName->getNamespace();
757 }
758 
getLocalName()759 OUString SAL_CALL librdf_NamedGraph::getLocalName()
760 {
761     return m_xName->getLocalName();
762 }
763 
764 // css::rdf::XNamedGraph:
getName()765 uno::Reference< rdf::XURI > SAL_CALL librdf_NamedGraph::getName()
766 {
767     return m_xName;
768 }
769 
clear()770 void SAL_CALL librdf_NamedGraph::clear()
771 {
772     uno::Reference< rdf::XRepository > xRep( m_wRep );
773     if (!xRep.is()) {
774         throw rdf::RepositoryException(
775             "librdf_NamedGraph::clear: repository is gone", *this);
776     }
777     const OUString contextU( m_xName->getStringValue() );
778     try {
779         m_pRep->clearGraph_NoLock(contextU);
780     } catch (lang::IllegalArgumentException & ex) {
781         css::uno::Any anyEx = cppu::getCaughtException();
782         throw lang::WrappedTargetRuntimeException( ex.Message,
783                         *this, anyEx );
784     }
785     ::osl::MutexGuard g(m_CacheMutex);
786     m_aStatementsCache.clear();
787 }
788 
addStatement(const uno::Reference<rdf::XResource> & i_xSubject,const uno::Reference<rdf::XURI> & i_xPredicate,const uno::Reference<rdf::XNode> & i_xObject)789 void SAL_CALL librdf_NamedGraph::addStatement(
790     const uno::Reference< rdf::XResource > & i_xSubject,
791     const uno::Reference< rdf::XURI > & i_xPredicate,
792     const uno::Reference< rdf::XNode > & i_xObject)
793 {
794     uno::Reference< rdf::XRepository > xRep( m_wRep );
795     if (!xRep.is()) {
796         throw rdf::RepositoryException(
797             "librdf_NamedGraph::addStatement: repository is gone", *this);
798     }
799     {
800         ::osl::MutexGuard g(m_CacheMutex);
801         m_aStatementsCache.clear();
802     }
803     m_pRep->addStatementGraph_NoLock(
804             i_xSubject, i_xPredicate, i_xObject, m_xName);
805 }
806 
removeStatements(const uno::Reference<rdf::XResource> & i_xSubject,const uno::Reference<rdf::XURI> & i_xPredicate,const uno::Reference<rdf::XNode> & i_xObject)807 void SAL_CALL librdf_NamedGraph::removeStatements(
808     const uno::Reference< rdf::XResource > & i_xSubject,
809     const uno::Reference< rdf::XURI > & i_xPredicate,
810     const uno::Reference< rdf::XNode > & i_xObject)
811 {
812     uno::Reference< rdf::XRepository > xRep( m_wRep );
813     if (!xRep.is()) {
814         throw rdf::RepositoryException(
815             "librdf_NamedGraph::removeStatements: repository is gone", *this);
816     }
817     {
818         ::osl::MutexGuard g(m_CacheMutex);
819         m_aStatementsCache.clear();
820     }
821     m_pRep->removeStatementsGraph_NoLock(
822             i_xSubject, i_xPredicate, i_xObject, m_xName);
823 }
824 
createCacheKey_NoLock(const uno::Reference<rdf::XResource> & i_xSubject,const uno::Reference<rdf::XURI> & i_xPredicate,const uno::Reference<rdf::XNode> & i_xObject)825 OUString librdf_NamedGraph::createCacheKey_NoLock(
826     const uno::Reference< rdf::XResource > & i_xSubject,
827     const uno::Reference< rdf::XURI > & i_xPredicate,
828     const uno::Reference< rdf::XNode > & i_xObject)
829 {
830     OUStringBuffer cacheKey(256);
831     librdf_TypeConverter::extractResourceToCacheKey_NoLock(i_xSubject, cacheKey);
832     cacheKey.append("\t");
833     librdf_TypeConverter::extractResourceToCacheKey_NoLock(i_xPredicate, cacheKey);
834     cacheKey.append("\t");
835     librdf_TypeConverter::extractNodeToCacheKey_NoLock(i_xObject, cacheKey);
836     return cacheKey.makeStringAndClear();
837 }
838 
839 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)840 librdf_NamedGraph::getStatements(
841     const uno::Reference< rdf::XResource > & i_xSubject,
842     const uno::Reference< rdf::XURI > & i_xPredicate,
843     const uno::Reference< rdf::XNode > & i_xObject)
844 {
845     OUString cacheKey = createCacheKey_NoLock(i_xSubject, i_xPredicate, i_xObject);
846     {
847         ::osl::MutexGuard g(m_CacheMutex);
848         auto it = m_aStatementsCache.find(cacheKey);
849         if (it != m_aStatementsCache.end()) {
850             return new librdf_GraphResult2(it->second);
851         }
852     }
853 
854     uno::Reference< rdf::XRepository > xRep( m_wRep );
855     if (!xRep.is()) {
856         throw rdf::RepositoryException(
857             "librdf_NamedGraph::getStatements: repository is gone", *this);
858     }
859     std::vector<rdf::Statement> vStatements = m_pRep->getStatementsGraph_NoLock(
860             i_xSubject, i_xPredicate, i_xObject, m_xName);
861 
862     {
863         ::osl::MutexGuard g(m_CacheMutex);
864         m_aStatementsCache.emplace(cacheKey, vStatements);
865     }
866     return new librdf_GraphResult2(vStatements);
867 }
868 
869 
870 std::shared_ptr<librdf_world> librdf_Repository::m_pWorld;
871 sal_uInt32 librdf_Repository::m_NumInstances = 0;
872 osl::Mutex librdf_Repository::m_aMutex;
873 
librdf_Repository(uno::Reference<uno::XComponentContext> const & i_xContext)874 librdf_Repository::librdf_Repository(
875         uno::Reference< uno::XComponentContext > const & i_xContext)
876     : /*BaseMutex(),*/ m_xContext(i_xContext)
877 //    m_pWorld  (static_cast<librdf_world  *>(0), safe_librdf_free_world  ),
878     , m_pStorage(static_cast<librdf_storage*>(nullptr), safe_librdf_free_storage)
879     , m_pModel  (static_cast<librdf_model  *>(nullptr), safe_librdf_free_model  )
880     , m_NamedGraphs()
881     , m_TypeConverter(i_xContext, *this)
882 {
883     OSL_ENSURE(i_xContext.is(), "librdf_Repository: null context");
884 
885     ::osl::MutexGuard g(m_aMutex);
886     if (!m_NumInstances++) {
887         m_pWorld.reset(m_TypeConverter.createWorld_Lock(),
888                 safe_librdf_free_world);
889     }
890 }
891 
~librdf_Repository()892 librdf_Repository::~librdf_Repository()
893 {
894     ::osl::MutexGuard g(m_aMutex);
895 
896     // must destroy these before world!
897     m_pModel.reset();
898     m_pStorage.reset();
899 
900     // FIXME: so it turns out that calling librdf_free_world will
901     //   (via raptor_sax2_finish) call xmlCleanupParser, which will
902     //   free libxml2's globals! ARRRGH!!! => never call librdf_free_world
903 #if 0
904     if (!--m_NumInstances) {
905         m_pWorld.reset();
906     }
907 #endif
908 }
909 
910 // com.sun.star.uno.XServiceInfo:
getImplementationName()911 OUString SAL_CALL librdf_Repository::getImplementationName()
912 {
913     return "librdf_Repository";
914 }
915 
supportsService(OUString const & serviceName)916 sal_Bool SAL_CALL librdf_Repository::supportsService(
917     OUString const & serviceName)
918 {
919     return cppu::supportsService(this, serviceName);
920 }
921 
922 uno::Sequence< OUString > SAL_CALL
getSupportedServiceNames()923 librdf_Repository::getSupportedServiceNames()
924 {
925     return { "com.sun.star.rdf.Repository" };
926 }
927 
928 // css::rdf::XRepository:
createBlankNode()929 uno::Reference< rdf::XBlankNode > SAL_CALL librdf_Repository::createBlankNode()
930 {
931     ::osl::MutexGuard g(m_aMutex);
932     const std::shared_ptr<librdf_node> pNode(
933         librdf_new_node_from_blank_identifier(m_pWorld.get(), nullptr),
934         safe_librdf_free_node);
935     if (!pNode) {
936         throw uno::RuntimeException(
937             "librdf_Repository::createBlankNode: "
938             "librdf_new_node_from_blank_identifier failed", *this);
939     }
940     const unsigned char * id (librdf_node_get_blank_identifier(pNode.get()));
941     if (!id) {
942         throw uno::RuntimeException(
943             "librdf_Repository::createBlankNode: "
944             "librdf_node_get_blank_identifier failed", *this);
945     }
946     const OUString nodeID(OUString::createFromAscii(
947         reinterpret_cast<const char *>(id)));
948     try {
949         return rdf::BlankNode::create(m_xContext, nodeID);
950     } catch (const lang::IllegalArgumentException &) {
951         css::uno::Any anyEx = cppu::getCaughtException();
952         throw lang::WrappedTargetRuntimeException(
953                 "librdf_Repository::createBlankNode: "
954                 "illegal blank node label", *this, anyEx);
955     }
956 }
957 
958 //void SAL_CALL
959 uno::Reference<rdf::XNamedGraph> 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)960 librdf_Repository::importGraph(::sal_Int16 i_Format,
961     const uno::Reference< io::XInputStream > & i_xInStream,
962     const uno::Reference< rdf::XURI > & i_xGraphName,
963     const uno::Reference< rdf::XURI > & i_xBaseURI)
964 {
965     if (!i_xInStream.is()) {
966         throw lang::IllegalArgumentException(
967             "librdf_Repository::importGraph: stream is null", *this, 1);
968     }
969     //FIXME: other formats
970     if (i_Format != rdf::FileFormat::RDF_XML) {
971         throw datatransfer::UnsupportedFlavorException(
972                 "librdf_Repository::importGraph: file format not supported", *this);
973     }
974     if (!i_xGraphName.is()) {
975         throw lang::IllegalArgumentException(
976                 "librdf_Repository::importGraph: graph name is null", *this, 2);
977     }
978     if (i_xGraphName->getStringValue().startsWith(s_nsOOo))
979     {
980         throw lang::IllegalArgumentException(
981                 "librdf_Repository::importGraph: URI is reserved", *this, 0);
982     }
983     if (!i_xBaseURI.is()) { //FIXME: any i_Format that don't need a base URI?
984         throw lang::IllegalArgumentException(
985                 "librdf_Repository::importGraph: base URI is null", *this, 3);
986     }
987     OSL_ENSURE(i_xBaseURI.is(), "no base uri");
988     const OUString baseURIU( i_xBaseURI->getStringValue() );
989     if (baseURIU.indexOf('#') >= 0) {
990         throw lang::IllegalArgumentException(
991                 "librdf_Repository::importGraph: base URI is not absolute", *this, 3);
992     }
993 
994     const OUString contextU( i_xGraphName->getStringValue() );
995 
996     uno::Sequence<sal_Int8> buf;
997     uno::Reference<io::XSeekable> xSeekable(i_xInStream, uno::UNO_QUERY);
998     // UGLY: if only redland could read streams...
999     const sal_Int64 sz( xSeekable.is() ? xSeekable->getLength() : 1 << 20 );
1000     // exceptions are propagated
1001     i_xInStream->readBytes( buf, static_cast<sal_Int32>( sz ) );
1002 
1003     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1004 
1005     if (m_NamedGraphs.find(contextU) != m_NamedGraphs.end()) {
1006         throw container::ElementExistException(
1007                 "librdf_Repository::importGraph: graph with given URI exists", *this);
1008     }
1009     const OString context(
1010         OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) );
1011 
1012     const std::shared_ptr<librdf_node> pContext(
1013         librdf_new_node_from_uri_string(m_pWorld.get(),
1014             reinterpret_cast<const unsigned char*> (context.getStr())),
1015         safe_librdf_free_node);
1016     if (!pContext) {
1017         throw uno::RuntimeException(
1018             "librdf_Repository::importGraph: librdf_new_node_from_uri_string failed", *this);
1019     }
1020 
1021     const OString baseURI(
1022         OUStringToOString(baseURIU, RTL_TEXTENCODING_UTF8) );
1023     const std::shared_ptr<librdf_uri> pBaseURI(
1024         librdf_new_uri(m_pWorld.get(),
1025             reinterpret_cast<const unsigned char*> (baseURI.getStr())),
1026         safe_librdf_free_uri);
1027     if (!pBaseURI) {
1028         throw uno::RuntimeException( "librdf_Repository::importGraph: librdf_new_uri failed", *this);
1029     }
1030 
1031     const std::shared_ptr<librdf_parser> pParser(
1032         librdf_new_parser(m_pWorld.get(), "rdfxml", nullptr, nullptr),
1033         safe_librdf_free_parser);
1034     if (!pParser) {
1035         throw uno::RuntimeException(
1036                 "librdf_Repository::importGraph: "
1037                 "librdf_new_parser failed", *this);
1038     }
1039 
1040     const std::shared_ptr<librdf_stream> pStream(
1041         librdf_parser_parse_counted_string_as_stream(pParser.get(),
1042             reinterpret_cast<const unsigned char*>(buf.getConstArray()),
1043             buf.getLength(), pBaseURI.get()),
1044         safe_librdf_free_stream);
1045     if (!pStream) {
1046         throw rdf::ParseException(
1047             "librdf_Repository::importGraph: "
1048             "librdf_parser_parse_counted_string_as_stream failed", *this);
1049     }
1050     rtl::Reference<librdf_NamedGraph> const pGraph(
1051         new librdf_NamedGraph(this, i_xGraphName));
1052     m_NamedGraphs.insert(std::make_pair(contextU, pGraph));
1053     if (librdf_model_context_add_statements(m_pModel.get(),
1054             pContext.get(), pStream.get())) {
1055         throw rdf::RepositoryException(
1056             "librdf_Repository::importGraph: "
1057             "librdf_model_context_add_statements failed", *this);
1058     }
1059 
1060     return pGraph;
1061 }
1062 
addChaffWhenEncryptedStorage(const uno::Reference<io::XOutputStream> & rStream,unsigned char * pBuffer,size_t length)1063 void addChaffWhenEncryptedStorage(const uno::Reference< io::XOutputStream > &rStream, unsigned char* pBuffer, size_t length)
1064 {
1065     if (!length)
1066         return;
1067 
1068     uno::Reference< embed::XEncryptionProtectedSource2 > xEncr(rStream,
1069         uno::UNO_QUERY);
1070 
1071     bool bAddChaff = xEncr.is() && xEncr->hasEncryptionData();
1072 
1073     // exceptions are propagated
1074     if (!bAddChaff)
1075     {
1076         const uno::Sequence<sal_Int8> buf(
1077             reinterpret_cast<sal_Int8*>(pBuffer), length);
1078         rStream->writeBytes(buf);
1079     }
1080     else
1081     {
1082         unsigned char *postcomment =
1083             reinterpret_cast<unsigned char*>(strchr(reinterpret_cast<char*>(pBuffer), '\n'));
1084         if (postcomment != nullptr)
1085         {
1086             ++postcomment;
1087 
1088             size_t preamblelen = postcomment - pBuffer;
1089 
1090             uno::Sequence<sal_Int8> buf(
1091                 reinterpret_cast<sal_Int8*>(pBuffer), preamblelen);
1092             rStream->writeBytes(buf);
1093 
1094             OString aComment =
1095                 "<!--" +
1096                 comphelper::xml::makeXMLChaff() +
1097                 "-->";
1098 
1099             buf = uno::Sequence<sal_Int8>(
1100                 reinterpret_cast<const sal_Int8*>(aComment.getStr()), aComment.getLength());
1101             rStream->writeBytes(buf);
1102 
1103             buf = uno::Sequence<sal_Int8>(
1104                 reinterpret_cast<sal_Int8*>(postcomment), length-preamblelen);
1105             rStream->writeBytes(buf);
1106         }
1107     }
1108 }
1109 
1110 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)1111 librdf_Repository::exportGraph(::sal_Int16 i_Format,
1112     const uno::Reference< io::XOutputStream > & i_xOutStream,
1113     const uno::Reference< rdf::XURI > & i_xGraphName,
1114     const uno::Reference< rdf::XURI > & i_xBaseURI)
1115 {
1116     if (!i_xOutStream.is()) {
1117         throw lang::IllegalArgumentException(
1118                 "librdf_Repository::exportGraph: stream is null", *this, 1);
1119     }
1120     // FIXME: other formats
1121     if (i_Format != rdf::FileFormat::RDF_XML) {
1122         throw datatransfer::UnsupportedFlavorException(
1123                 "librdf_Repository::exportGraph: "
1124                 "file format not supported", *this);
1125     }
1126     if (!i_xGraphName.is()) {
1127         throw lang::IllegalArgumentException(
1128                 "librdf_Repository::exportGraph: "
1129                 "graph name is null", *this, 2);
1130     }
1131     if (!i_xBaseURI.is()) { //FIXME: any i_Format that don't need a base URI?
1132         throw lang::IllegalArgumentException(
1133                 "librdf_Repository::exportGraph: "
1134                 "base URI is null", *this, 3);
1135     }
1136     OSL_ENSURE(i_xBaseURI.is(), "no base uri");
1137     const OUString baseURIU( i_xBaseURI->getStringValue() );
1138     if (baseURIU.indexOf('#') >= 0) {
1139         throw lang::IllegalArgumentException(
1140                 "librdf_Repository::exportGraph: "
1141                 "base URI is not absolute", *this, 3);
1142     }
1143 
1144     const OUString contextU( i_xGraphName->getStringValue() );
1145 
1146     ::osl::ClearableMutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1147 
1148     if (m_NamedGraphs.find(contextU) == m_NamedGraphs.end()) {
1149         throw container::NoSuchElementException(
1150                 "librdf_Repository::exportGraph: "
1151                 "no graph with given URI exists", *this);
1152     }
1153     const OString context(
1154         OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) );
1155 
1156     const std::shared_ptr<librdf_node> pContext(
1157         librdf_new_node_from_uri_string(m_pWorld.get(),
1158             reinterpret_cast<const unsigned char*> (context.getStr())),
1159         safe_librdf_free_node);
1160     if (!pContext) {
1161         throw uno::RuntimeException(
1162             "librdf_Repository::exportGraph: "
1163             "librdf_new_node_from_uri_string failed", *this);
1164     }
1165     const OString baseURI(
1166         OUStringToOString(baseURIU, RTL_TEXTENCODING_UTF8) );
1167     const std::shared_ptr<librdf_uri> pBaseURI(
1168         librdf_new_uri(m_pWorld.get(),
1169             reinterpret_cast<const unsigned char*> (baseURI.getStr())),
1170         safe_librdf_free_uri);
1171     if (!pBaseURI) {
1172         throw uno::RuntimeException(
1173             "librdf_Repository::exportGraph: "
1174             "librdf_new_uri failed", *this);
1175     }
1176 
1177     const std::shared_ptr<librdf_stream> pStream(
1178         librdf_model_context_as_stream(m_pModel.get(), pContext.get()),
1179         safe_librdf_free_stream);
1180     if (!pStream) {
1181         throw rdf::RepositoryException(
1182             "librdf_Repository::exportGraph: "
1183             "librdf_model_context_as_stream failed", *this);
1184     }
1185     const char * const format("rdfxml");
1186     // #i116443#: abbrev breaks when certain URIs are used as data types
1187 //    const char *format("rdfxml-abbrev");
1188     const std::shared_ptr<librdf_serializer> pSerializer(
1189         librdf_new_serializer(m_pWorld.get(), format, nullptr, nullptr),
1190         safe_librdf_free_serializer);
1191     if (!pSerializer) {
1192         throw uno::RuntimeException(
1193             "librdf_Repository::exportGraph: "
1194             "librdf_new_serializer failed", *this);
1195     }
1196 
1197     const std::shared_ptr<librdf_uri> pRelativeURI(
1198         librdf_new_uri(m_pWorld.get(), reinterpret_cast<const unsigned char*>
1199                 ("http://feature.librdf.org/raptor-relativeURIs")),
1200                  safe_librdf_free_uri);
1201     const std::shared_ptr<librdf_uri> pWriteBaseURI(
1202         librdf_new_uri(m_pWorld.get(), reinterpret_cast<const unsigned char*>
1203             ("http://feature.librdf.org/raptor-writeBaseURI")),
1204              safe_librdf_free_uri);
1205     const std::shared_ptr<librdf_node> p0(
1206         librdf_new_node_from_literal(m_pWorld.get(),
1207             reinterpret_cast<const unsigned char*> ("0"), nullptr, 0),
1208         safe_librdf_free_node);
1209     const std::shared_ptr<librdf_node> p1(
1210         librdf_new_node_from_literal(m_pWorld.get(),
1211             reinterpret_cast<const unsigned char*> ("1"), nullptr, 0),
1212         safe_librdf_free_node);
1213     if (!pWriteBaseURI || !pRelativeURI || !p0 || !p1) {
1214         throw uno::RuntimeException(
1215             "librdf_Repository::exportGraph: "
1216             "librdf_new_uri or librdf_new_node_from_literal failed", *this);
1217     }
1218 
1219     // make URIs relative to base URI
1220     if (librdf_serializer_set_feature(pSerializer.get(),
1221         pRelativeURI.get(), p1.get()))
1222     {
1223         throw uno::RuntimeException(
1224             "librdf_Repository::exportGraph: "
1225             "librdf_serializer_set_feature relativeURIs failed", *this);
1226     }
1227     // but do not write the base URI to the file!
1228     if (librdf_serializer_set_feature(pSerializer.get(),
1229         pWriteBaseURI.get(), p0.get()))
1230     {
1231         throw uno::RuntimeException(
1232             "librdf_Repository::exportGraph: "
1233             "librdf_serializer_set_feature writeBaseURI failed", *this);
1234     }
1235 
1236     size_t length;
1237     const std::shared_ptr<unsigned char> pBuf(
1238         librdf_serializer_serialize_stream_to_counted_string(
1239             pSerializer.get(), pBaseURI.get(), pStream.get(), &length), free);
1240     if (!pBuf) {
1241         throw rdf::RepositoryException(
1242             "librdf_Repository::exportGraph: "
1243             "librdf_serializer_serialize_stream_to_counted_string failed",
1244             *this);
1245     }
1246 
1247     g.clear(); // release Mutex before calling i_xOutStream methods
1248 
1249     addChaffWhenEncryptedStorage(i_xOutStream, pBuf.get(), length);
1250 }
1251 
1252 uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL
getGraphNames()1253 librdf_Repository::getGraphNames()
1254 {
1255     ::osl::MutexGuard g(m_aMutex);
1256     ::std::vector< uno::Reference<rdf::XURI> > ret;
1257     std::transform(m_NamedGraphs.begin(), m_NamedGraphs.end(),
1258         std::back_inserter(ret),
1259         [](std::pair<OUString, ::rtl::Reference<librdf_NamedGraph>> const& it)
1260             { return it.second->getName(); });
1261     return comphelper::containerToSequence(ret);
1262 }
1263 
1264 uno::Reference< rdf::XNamedGraph > SAL_CALL
getGraph(const uno::Reference<rdf::XURI> & i_xGraphName)1265 librdf_Repository::getGraph(const uno::Reference< rdf::XURI > & i_xGraphName)
1266 {
1267     if (!i_xGraphName.is()) {
1268         throw lang::IllegalArgumentException(
1269                 "librdf_Repository::getGraph: URI is null", *this, 0);
1270     }
1271     const OUString contextU( i_xGraphName->getStringValue() );
1272 
1273     ::osl::MutexGuard g(m_aMutex);
1274     const NamedGraphMap_t::iterator iter( m_NamedGraphs.find(contextU) );
1275     if (iter != m_NamedGraphs.end()) {
1276         return iter->second;
1277     } else {
1278         return nullptr;
1279     }
1280 }
1281 
1282 uno::Reference< rdf::XNamedGraph > SAL_CALL
createGraph(const uno::Reference<rdf::XURI> & i_xGraphName)1283 librdf_Repository::createGraph(const uno::Reference< rdf::XURI > & i_xGraphName)
1284 {
1285     if (!i_xGraphName.is()) {
1286         throw lang::IllegalArgumentException(
1287                 "librdf_Repository::createGraph: URI is null", *this, 0);
1288     }
1289 
1290     const OUString contextU( i_xGraphName->getStringValue() );
1291     if (contextU.startsWith(s_nsOOo))
1292     {
1293         throw lang::IllegalArgumentException(
1294                 "librdf_Repository::createGraph: URI is reserved", *this, 0);
1295     }
1296 
1297     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1298 
1299     // NB: librdf does not have a concept of graphs as such;
1300     //     a librdf named graph exists iff the model contains a statement with
1301     //     the graph name as context
1302 
1303     if (m_NamedGraphs.find(contextU) != m_NamedGraphs.end()) {
1304         throw container::ElementExistException(
1305                 "librdf_Repository::createGraph: graph with given URI exists", *this);
1306     }
1307     m_NamedGraphs.insert(std::make_pair(contextU,
1308         new librdf_NamedGraph(this, i_xGraphName)));
1309     return m_NamedGraphs.find(contextU)->second;
1310 }
1311 
1312 void SAL_CALL
destroyGraph(const uno::Reference<rdf::XURI> & i_xGraphName)1313 librdf_Repository::destroyGraph(
1314         const uno::Reference< rdf::XURI > & i_xGraphName)
1315 {
1316     if (!i_xGraphName.is()) {
1317         throw lang::IllegalArgumentException(
1318                 "librdf_Repository::destroyGraph: URI is null", *this, 0);
1319     }
1320     const OUString contextU( i_xGraphName->getStringValue() );
1321 
1322     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1323 
1324     const NamedGraphMap_t::iterator iter( clearGraph_Lock(contextU, false) );
1325     m_NamedGraphs.erase(iter);
1326 }
1327 
isMetadatableWithoutMetadata(uno::Reference<uno::XInterface> const & i_xNode)1328 bool isMetadatableWithoutMetadata(
1329     uno::Reference<uno::XInterface> const & i_xNode)
1330 {
1331     const uno::Reference<rdf::XMetadatable> xMeta( i_xNode, uno::UNO_QUERY );
1332     return (xMeta.is() && xMeta->getMetadataReference().Second.isEmpty());
1333 }
1334 
1335 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)1336 librdf_Repository::getStatements(
1337     const uno::Reference< rdf::XResource > & i_xSubject,
1338     const uno::Reference< rdf::XURI > & i_xPredicate,
1339     const uno::Reference< rdf::XNode > & i_xObject)
1340 {
1341     if (isMetadatableWithoutMetadata(i_xSubject)   ||
1342         isMetadatableWithoutMetadata(i_xPredicate) ||
1343         isMetadatableWithoutMetadata(i_xObject))
1344     {
1345         return new librdf_GraphResult(this, m_aMutex,
1346             std::shared_ptr<librdf_stream>(),
1347             std::shared_ptr<librdf_node>());
1348     }
1349 
1350     librdf_TypeConverter::Statement const stmt(
1351         librdf_TypeConverter::extractStatement_NoLock(
1352             i_xSubject, i_xPredicate, i_xObject));
1353 
1354     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1355 
1356     const std::shared_ptr<librdf_statement> pStatement(
1357         librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt),
1358         safe_librdf_free_statement);
1359     OSL_ENSURE(pStatement, "mkStatement failed");
1360 
1361     const std::shared_ptr<librdf_stream> pStream(
1362         librdf_model_find_statements(m_pModel.get(), pStatement.get()),
1363         safe_librdf_free_stream);
1364     if (!pStream) {
1365         throw rdf::RepositoryException(
1366             "librdf_Repository::getStatements: "
1367             "librdf_model_find_statements failed", *this);
1368     }
1369 
1370     return new librdf_GraphResult(this, m_aMutex, pStream,
1371         std::shared_ptr<librdf_node>());
1372 }
1373 
1374 
1375 uno::Reference< rdf::XQuerySelectResult > SAL_CALL
querySelect(const OUString & i_rQuery)1376 librdf_Repository::querySelect(const OUString & i_rQuery)
1377 {
1378     ::osl::MutexGuard g(m_aMutex);
1379     const OString query(
1380         OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) );
1381     const std::shared_ptr<librdf_query> pQuery(
1382         librdf_new_query(m_pWorld.get(), s_sparql, nullptr,
1383             reinterpret_cast<const unsigned char*> (query.getStr()), nullptr),
1384         safe_librdf_free_query);
1385     if (!pQuery) {
1386         throw rdf::QueryException(
1387             "librdf_Repository::querySelect: "
1388             "librdf_new_query failed", *this);
1389     }
1390     const std::shared_ptr<librdf_query_results> pResults(
1391         librdf_model_query_execute(m_pModel.get(), pQuery.get()),
1392         safe_librdf_free_query_results);
1393     if (!pResults || !librdf_query_results_is_bindings(pResults.get())) {
1394         throw rdf::QueryException(
1395             "librdf_Repository::querySelect: "
1396             "query result is null or not bindings", *this);
1397     }
1398 
1399     const int count( librdf_query_results_get_bindings_count(pResults.get()) );
1400     if (count < 0) {
1401         throw rdf::QueryException(
1402             "librdf_Repository::querySelect: "
1403             "librdf_query_results_get_bindings_count failed", *this);
1404     }
1405     uno::Sequence< OUString > names(count);
1406     for (int i = 0; i < count; ++i) {
1407         const char* name( librdf_query_results_get_binding_name(
1408             pResults.get(), i) );
1409         if (!name) {
1410             throw rdf::QueryException(
1411                 "librdf_Repository::querySelect: binding is null", *this);
1412         }
1413 
1414         names[i] = OUString::createFromAscii(name);
1415     }
1416 
1417     return new librdf_QuerySelectResult(this, m_aMutex,
1418         pQuery, pResults, names);
1419 }
1420 
1421 uno::Reference< container::XEnumeration > SAL_CALL
queryConstruct(const OUString & i_rQuery)1422 librdf_Repository::queryConstruct(const OUString & i_rQuery)
1423 {
1424     ::osl::MutexGuard g(m_aMutex);
1425     const OString query(
1426         OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) );
1427     const std::shared_ptr<librdf_query> pQuery(
1428         librdf_new_query(m_pWorld.get(), s_sparql, nullptr,
1429             reinterpret_cast<const unsigned char*> (query.getStr()), nullptr),
1430         safe_librdf_free_query);
1431     if (!pQuery) {
1432         throw rdf::QueryException(
1433             "librdf_Repository::queryConstruct: "
1434             "librdf_new_query failed", *this);
1435     }
1436     const std::shared_ptr<librdf_query_results> pResults(
1437         librdf_model_query_execute(m_pModel.get(), pQuery.get()),
1438         safe_librdf_free_query_results);
1439     if (!pResults || !librdf_query_results_is_graph(pResults.get())) {
1440         throw rdf::QueryException(
1441             "librdf_Repository::queryConstruct: "
1442             "query result is null or not graph", *this);
1443     }
1444     const std::shared_ptr<librdf_stream> pStream(
1445         librdf_query_results_as_stream(pResults.get()),
1446         safe_librdf_free_stream);
1447     if (!pStream) {
1448         throw rdf::QueryException(
1449             "librdf_Repository::queryConstruct: "
1450             "librdf_query_results_as_stream failed", *this);
1451     }
1452 
1453     return new librdf_GraphResult(this, m_aMutex, pStream,
1454                                   std::shared_ptr<librdf_node>(), pQuery);
1455 }
1456 
1457 sal_Bool SAL_CALL
queryAsk(const OUString & i_rQuery)1458 librdf_Repository::queryAsk(const OUString & i_rQuery)
1459 {
1460     ::osl::MutexGuard g(m_aMutex);
1461 
1462     const OString query(
1463         OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) );
1464     const std::shared_ptr<librdf_query> pQuery(
1465         librdf_new_query(m_pWorld.get(), s_sparql, nullptr,
1466             reinterpret_cast<const unsigned char*> (query.getStr()), nullptr),
1467         safe_librdf_free_query);
1468     if (!pQuery) {
1469         throw rdf::QueryException(
1470             "librdf_Repository::queryAsk: "
1471             "librdf_new_query failed", *this);
1472     }
1473     const std::shared_ptr<librdf_query_results> pResults(
1474         librdf_model_query_execute(m_pModel.get(), pQuery.get()),
1475         safe_librdf_free_query_results);
1476     if (!pResults || !librdf_query_results_is_boolean(pResults.get())) {
1477         throw rdf::QueryException(
1478             "librdf_Repository::queryAsk: "
1479             "query result is null or not boolean", *this);
1480     }
1481     return bool(librdf_query_results_get_boolean(pResults.get()));
1482 }
1483 
1484 // css::rdf::XDocumentRepository:
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)1485 void SAL_CALL librdf_Repository::setStatementRDFa(
1486     const uno::Reference< rdf::XResource > & i_xSubject,
1487     const uno::Sequence< uno::Reference< rdf::XURI > > & i_rPredicates,
1488     const uno::Reference< rdf::XMetadatable > & i_xObject,
1489     const OUString & i_rRDFaContent,
1490     const uno::Reference< rdf::XURI > & i_xRDFaDatatype)
1491 {
1492     if (!i_xSubject.is()) {
1493         throw lang::IllegalArgumentException(
1494             "librdf_Repository::setStatementRDFa: Subject is null", *this, 0);
1495     }
1496     if (!i_rPredicates.hasElements()) {
1497         throw lang::IllegalArgumentException(
1498             "librdf_Repository::setStatementRDFa: no Predicates",
1499             *this, 1);
1500     }
1501     if (std::any_of(i_rPredicates.begin(), i_rPredicates.end(),
1502             [](const uno::Reference< rdf::XURI >& rPredicate) { return !rPredicate.is(); })) {
1503         throw lang::IllegalArgumentException(
1504             "librdf_Repository::setStatementRDFa: Predicate is null", *this, 1);
1505     }
1506     if (!i_xObject.is()) {
1507         throw lang::IllegalArgumentException(
1508             "librdf_Repository::setStatementRDFa: Object is null", *this, 2);
1509     }
1510     const uno::Reference<lang::XServiceInfo> xService(i_xObject,
1511         uno::UNO_QUERY_THROW);
1512     uno::Reference<text::XTextRange> xTextRange;
1513     if (xService->supportsService("com.sun.star.table.Cell") ||
1514         xService->supportsService("com.sun.star.text.CellProperties") || // for writer
1515         xService->supportsService("com.sun.star.text.Paragraph"))
1516     {
1517         xTextRange.set(i_xObject, uno::UNO_QUERY_THROW);
1518     }
1519     else if (xService->supportsService("com.sun.star.text.Bookmark") ||
1520              xService->supportsService("com.sun.star.text.InContentMetadata"))
1521     {
1522         const uno::Reference<text::XTextContent> xTextContent(i_xObject,
1523             uno::UNO_QUERY_THROW);
1524         xTextRange = xTextContent->getAnchor();
1525     }
1526     if (!xTextRange.is()) {
1527         throw lang::IllegalArgumentException(
1528             "librdf_Repository::setStatementRDFa: "
1529             "Object does not support RDFa", *this, 2);
1530     }
1531     // ensure that the metadatable has an XML ID
1532     i_xObject->ensureMetadataReference();
1533     const beans::StringPair mdref( i_xObject->getMetadataReference() );
1534     if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) {
1535         throw uno::RuntimeException(
1536                 "librdf_Repository::setStatementRDFa: "
1537                 "ensureMetadataReference did not", *this);
1538     }
1539     OUString const sXmlId(mdref.First + "#" + mdref.Second);
1540     OUString const sContext(s_nsOOo + sXmlId);
1541     OUString const content( (i_rRDFaContent.isEmpty())
1542             ? xTextRange->getString()
1543             : i_rRDFaContent );
1544     uno::Reference<rdf::XNode> xContent;
1545     try {
1546         if (i_xRDFaDatatype.is()) {
1547             xContent.set(rdf::Literal::createWithType(m_xContext,
1548                     content, i_xRDFaDatatype),
1549                 uno::UNO_QUERY_THROW);
1550         } else {
1551             xContent.set(rdf::Literal::create(m_xContext, content),
1552                 uno::UNO_QUERY_THROW);
1553         }
1554     } catch (const lang::IllegalArgumentException &) {
1555         css::uno::Any anyEx = cppu::getCaughtException();
1556         throw lang::WrappedTargetRuntimeException(
1557                 "librdf_Repository::setStatementRDFa: "
1558                 "cannot create literal", *this, anyEx);
1559     }
1560 
1561     std::shared_ptr<librdf_TypeConverter::Resource> const pSubject(
1562         librdf_TypeConverter::extractResource_NoLock(i_xSubject));
1563     std::shared_ptr<librdf_TypeConverter::Node> const pContent(
1564         librdf_TypeConverter::extractNode_NoLock(xContent));
1565     ::std::vector< std::shared_ptr<librdf_TypeConverter::Resource> >
1566         predicates;
1567     ::std::transform(i_rPredicates.begin(), i_rPredicates.end(),
1568         ::std::back_inserter(predicates),
1569         [](uno::Reference<rdf::XURI> const& xURI)
1570             { return librdf_TypeConverter::extractResource_NoLock(xURI); });
1571 
1572     removeStatementRDFa(i_xObject); // not atomic with insertion?
1573 
1574     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1575 
1576     if (i_rRDFaContent.isEmpty()) {
1577         m_RDFaXHTMLContentSet.erase(sXmlId);
1578     } else {
1579         m_RDFaXHTMLContentSet.insert(sXmlId);
1580     }
1581     try
1582     {
1583         for (const auto& rPredicatePtr : predicates)
1584         {
1585             addStatementGraph_Lock(
1586                 librdf_TypeConverter::Statement(pSubject,
1587                     std::dynamic_pointer_cast<librdf_TypeConverter::URI>(rPredicatePtr),
1588                     pContent),
1589                 sContext, true);
1590         }
1591     }
1592     catch (const container::NoSuchElementException&)
1593     {
1594         css::uno::Any anyEx = cppu::getCaughtException();
1595         throw lang::WrappedTargetRuntimeException(
1596                 "librdf_Repository::setStatementRDFa: "
1597                 "cannot addStatementGraph", *this, anyEx);
1598     }
1599 }
1600 
removeStatementRDFa(const uno::Reference<rdf::XMetadatable> & i_xElement)1601 void SAL_CALL librdf_Repository::removeStatementRDFa(
1602     const uno::Reference< rdf::XMetadatable > & i_xElement)
1603 {
1604     if (!i_xElement.is()) {
1605         throw lang::IllegalArgumentException(
1606             "librdf_Repository::removeStatementRDFa: Element is null",
1607             *this, 0);
1608     }
1609 
1610     const beans::StringPair mdref( i_xElement->getMetadataReference() );
1611     if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) {
1612         return; // nothing to do...
1613     }
1614 
1615     OUString const sXmlId(s_nsOOo + mdref.First + "#" + mdref.Second);
1616 
1617     clearGraph_NoLock(sXmlId, true);
1618 }
1619 
1620 beans::Pair< uno::Sequence<rdf::Statement>, sal_Bool > SAL_CALL
getStatementRDFa(const uno::Reference<rdf::XMetadatable> & i_xElement)1621 librdf_Repository::getStatementRDFa(
1622     const uno::Reference< rdf::XMetadatable > & i_xElement)
1623 {
1624     if (!i_xElement.is()) {
1625         throw lang::IllegalArgumentException(
1626             "librdf_Repository::getStatementRDFa: Element is null", *this, 0);
1627     }
1628     const beans::StringPair mdref( i_xElement->getMetadataReference() );
1629     if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) {
1630         return beans::Pair< uno::Sequence<rdf::Statement>, sal_Bool >();
1631     }
1632     OUString const sXmlId(mdref.First + "#" + mdref.Second);
1633     uno::Reference<rdf::XURI> xXmlId;
1634     try {
1635         xXmlId.set( rdf::URI::create(m_xContext, s_nsOOo + sXmlId),
1636             uno::UNO_SET_THROW);
1637     } catch (const lang::IllegalArgumentException &) {
1638         css::uno::Any anyEx = cppu::getCaughtException();
1639         throw lang::WrappedTargetRuntimeException(
1640                 "librdf_Repository::getStatementRDFa: "
1641                 "cannot create URI for XML ID", *this, anyEx);
1642     }
1643 
1644     ::std::vector< rdf::Statement > ret;
1645     try
1646     {
1647         ret = getStatementsGraph_NoLock(nullptr, nullptr, nullptr, xXmlId, true);
1648     }
1649     catch (const container::NoSuchElementException&)
1650     {
1651         css::uno::Any anyEx = cppu::getCaughtException();
1652         throw lang::WrappedTargetRuntimeException(
1653                 "librdf_Repository::getStatementRDFa: "
1654                 "cannot getStatementsGraph", *this, anyEx);
1655     }
1656 
1657     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1658 
1659     return beans::Pair< uno::Sequence<rdf::Statement>, sal_Bool >(
1660             comphelper::containerToSequence(ret), 0 != m_RDFaXHTMLContentSet.count(sXmlId));
1661 }
1662 
1663 extern "C"
rdfa_context_stream_map_handler(librdf_stream * i_pStream,void *,librdf_statement * i_pStatement)1664 librdf_statement *rdfa_context_stream_map_handler(
1665     librdf_stream *i_pStream, void *, librdf_statement *i_pStatement)
1666 {
1667     OSL_ENSURE(i_pStream, "rdfa_context_stream_map_handler: stream null");
1668     if (i_pStream) {
1669         librdf_node *pCtxt(
1670 #if LIBRDF_VERSION >= 10012
1671             librdf_stream_get_context2(i_pStream) );
1672 #else
1673             static_cast<librdf_node *>(librdf_stream_get_context(i_pStream)) );
1674 #endif
1675         OSL_ENSURE(pCtxt, "rdfa_context_stream_map_handler: context null");
1676         if (pCtxt && isInternalContext(pCtxt)) {
1677             return i_pStatement;
1678         }
1679     }
1680     return nullptr;
1681 };
1682 
1683 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)1684 librdf_Repository::getStatementsRDFa(
1685     const uno::Reference< rdf::XResource > & i_xSubject,
1686     const uno::Reference< rdf::XURI > & i_xPredicate,
1687     const uno::Reference< rdf::XNode > & i_xObject)
1688 {
1689     if (isMetadatableWithoutMetadata(i_xSubject)   ||
1690         isMetadatableWithoutMetadata(i_xPredicate) ||
1691         isMetadatableWithoutMetadata(i_xObject))
1692     {
1693         return new librdf_GraphResult(this, m_aMutex,
1694             std::shared_ptr<librdf_stream>(),
1695             std::shared_ptr<librdf_node>());
1696     }
1697 
1698     librdf_TypeConverter::Statement const stmt(
1699         librdf_TypeConverter::extractStatement_NoLock(
1700             i_xSubject, i_xPredicate, i_xObject));
1701 
1702     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1703 
1704     const std::shared_ptr<librdf_statement> pStatement(
1705         librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt),
1706         safe_librdf_free_statement);
1707     OSL_ENSURE(pStatement, "mkStatement failed");
1708 
1709     const std::shared_ptr<librdf_stream> pStream(
1710         librdf_model_find_statements(m_pModel.get(), pStatement.get()),
1711         safe_librdf_free_stream);
1712     if (!pStream) {
1713         throw rdf::RepositoryException(
1714             "librdf_Repository::getStatementsRDFa: "
1715             "librdf_model_find_statements failed", *this);
1716     }
1717 
1718     if (librdf_stream_add_map(pStream.get(), rdfa_context_stream_map_handler,
1719                 nullptr, nullptr)) {
1720         throw rdf::RepositoryException(
1721             "librdf_Repository::getStatementsRDFa: "
1722             "librdf_stream_add_map failed", *this);
1723     }
1724 
1725     return new librdf_GraphResult(this, m_aMutex, pStream,
1726                                   std::shared_ptr<librdf_node>());
1727 }
1728 
1729 // css::lang::XInitialization:
initialize(const uno::Sequence<css::uno::Any> &)1730 void SAL_CALL librdf_Repository::initialize(
1731     const uno::Sequence< css::uno::Any > &)
1732 {
1733     ::osl::MutexGuard g(m_aMutex);
1734 
1735 //    m_pWorld.reset(m_TypeConverter.createWorld(), safe_librdf_free_world);
1736     m_pStorage.reset(m_TypeConverter.createStorage_Lock(m_pWorld.get()),
1737         safe_librdf_free_storage);
1738     m_pModel.reset(m_TypeConverter.createModel_Lock(
1739         m_pWorld.get(), m_pStorage.get()), safe_librdf_free_model);
1740 }
1741 
clearGraph_NoLock(OUString const & i_rGraphName,bool i_Internal)1742 NamedGraphMap_t::iterator librdf_Repository::clearGraph_NoLock(
1743         OUString const& i_rGraphName, bool i_Internal)
1744 //    throw (uno::RuntimeException, container::NoSuchElementException,
1745 //        rdf::RepositoryException)
1746 {
1747     ::osl::MutexGuard g(m_aMutex);
1748 
1749     return clearGraph_Lock(i_rGraphName, i_Internal);
1750 }
1751 
clearGraph_Lock(OUString const & i_rGraphName,bool i_Internal)1752 NamedGraphMap_t::iterator librdf_Repository::clearGraph_Lock(
1753         OUString const& i_rGraphName, bool i_Internal)
1754 {
1755     // internal: must be called with mutex locked!
1756     const NamedGraphMap_t::iterator iter( m_NamedGraphs.find(i_rGraphName) );
1757     if (!i_Internal && iter == m_NamedGraphs.end()) {
1758         throw container::NoSuchElementException(
1759                 "librdf_Repository::clearGraph: "
1760                 "no graph with given URI exists", *this);
1761     }
1762     const OString context(
1763         OUStringToOString(i_rGraphName, RTL_TEXTENCODING_UTF8) );
1764 
1765     const std::shared_ptr<librdf_node> pContext(
1766         librdf_new_node_from_uri_string(m_pWorld.get(),
1767             reinterpret_cast<const unsigned char*> (context.getStr())),
1768         safe_librdf_free_node);
1769     if (!pContext) {
1770         throw uno::RuntimeException(
1771             "librdf_Repository::clearGraph: "
1772             "librdf_new_node_from_uri_string failed", *this);
1773     }
1774     if (librdf_model_context_remove_statements(m_pModel.get(), pContext.get()))
1775     {
1776         throw rdf::RepositoryException(
1777             "librdf_Repository::clearGraph: "
1778             "librdf_model_context_remove_statements failed", *this);
1779     }
1780     return iter;
1781 }
1782 
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)1783 void librdf_Repository::addStatementGraph_NoLock(
1784     const uno::Reference< rdf::XResource > & i_xSubject,
1785     const uno::Reference< rdf::XURI > & i_xPredicate,
1786     const uno::Reference< rdf::XNode > & i_xObject,
1787     const uno::Reference< rdf::XURI > & i_xGraphName)
1788 //throw (uno::RuntimeException, lang::IllegalArgumentException,
1789 //    container::NoSuchElementException, rdf::RepositoryException)
1790 {
1791     if (!i_xSubject.is()) {
1792         throw lang::IllegalArgumentException(
1793             "librdf_Repository::addStatement: Subject is null", *this, 0);
1794     }
1795     if (!i_xPredicate.is()) {
1796         throw lang::IllegalArgumentException(
1797             "librdf_Repository::addStatement: Predicate is null",
1798             *this, 1);
1799     }
1800     if (!i_xObject.is()) {
1801         throw lang::IllegalArgumentException(
1802             "librdf_Repository::addStatement: Object is null", *this, 2);
1803     }
1804 
1805     librdf_TypeConverter::Statement const stmt(
1806         librdf_TypeConverter::extractStatement_NoLock(
1807             i_xSubject, i_xPredicate, i_xObject));
1808 
1809     const OUString contextU( i_xGraphName->getStringValue() );
1810 
1811     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1812 
1813     addStatementGraph_Lock(stmt, contextU, false/*i_Internal*/);
1814 }
1815 
addStatementGraph_Lock(librdf_TypeConverter::Statement const & i_rStatement,OUString const & i_rGraphName,bool i_Internal)1816 void librdf_Repository::addStatementGraph_Lock(
1817     librdf_TypeConverter::Statement const& i_rStatement,
1818     OUString const& i_rGraphName,
1819     bool i_Internal)
1820 {
1821     if (!i_Internal
1822         && (m_NamedGraphs.find(i_rGraphName) == m_NamedGraphs.end()))
1823     {
1824         throw container::NoSuchElementException(
1825                 "librdf_Repository::addStatement: "
1826                 "no graph with given URI exists", *this);
1827     }
1828     const OString context(
1829         OUStringToOString(i_rGraphName, RTL_TEXTENCODING_UTF8) );
1830 
1831     const std::shared_ptr<librdf_node> pContext(
1832         librdf_new_node_from_uri_string(m_pWorld.get(),
1833             reinterpret_cast<const unsigned char*> (context.getStr())),
1834         safe_librdf_free_node);
1835     if (!pContext) {
1836         throw uno::RuntimeException(
1837             "librdf_Repository::addStatement: "
1838             "librdf_new_node_from_uri_string failed", *this);
1839     }
1840     const std::shared_ptr<librdf_statement> pStatement(
1841         librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), i_rStatement),
1842         safe_librdf_free_statement);
1843     OSL_ENSURE(pStatement, "mkStatement failed");
1844 
1845     // Test for duplicate statement
1846     // librdf_model_add_statement disallows duplicates while
1847     // librdf_model_context_add_statement allows duplicates
1848     {
1849         const std::shared_ptr<librdf_stream> pStream(
1850             librdf_model_find_statements_in_context(m_pModel.get(),
1851                 pStatement.get(), pContext.get()),
1852             safe_librdf_free_stream);
1853         if (pStream && !librdf_stream_end(pStream.get()))
1854             return;
1855     }
1856 
1857     if (librdf_model_context_add_statement(m_pModel.get(),
1858             pContext.get(), pStatement.get())) {
1859         throw rdf::RepositoryException(
1860             "librdf_Repository::addStatement: "
1861             "librdf_model_context_add_statement failed", *this);
1862     }
1863 }
1864 
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)1865 void librdf_Repository::removeStatementsGraph_NoLock(
1866     const uno::Reference< rdf::XResource > & i_xSubject,
1867     const uno::Reference< rdf::XURI > & i_xPredicate,
1868     const uno::Reference< rdf::XNode > & i_xObject,
1869     const uno::Reference< rdf::XURI > & i_xGraphName)
1870 //throw (uno::RuntimeException, lang::IllegalArgumentException,
1871 //    container::NoSuchElementException, rdf::RepositoryException)
1872 {
1873     if (isMetadatableWithoutMetadata(i_xSubject)   ||
1874         isMetadatableWithoutMetadata(i_xPredicate) ||
1875         isMetadatableWithoutMetadata(i_xObject))
1876     {
1877         return;
1878     }
1879 
1880     librdf_TypeConverter::Statement const stmt(
1881         librdf_TypeConverter::extractStatement_NoLock(
1882             i_xSubject, i_xPredicate, i_xObject));
1883     const OUString contextU( i_xGraphName->getStringValue() );
1884 
1885     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1886 
1887     if (m_NamedGraphs.find(contextU) == m_NamedGraphs.end()) {
1888         throw container::NoSuchElementException(
1889                 "librdf_Repository::removeStatements: "
1890                 "no graph with given URI exists", *this);
1891     }
1892     const OString context(
1893         OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) );
1894 
1895     const std::shared_ptr<librdf_node> pContext(
1896         librdf_new_node_from_uri_string(m_pWorld.get(),
1897             reinterpret_cast<const unsigned char*> (context.getStr())),
1898         safe_librdf_free_node);
1899     if (!pContext) {
1900         throw uno::RuntimeException(
1901             "librdf_Repository::removeStatements: "
1902             "librdf_new_node_from_uri_string failed", *this);
1903     }
1904     const std::shared_ptr<librdf_statement> pStatement(
1905         librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt),
1906         safe_librdf_free_statement);
1907     OSL_ENSURE(pStatement, "mkStatement failed");
1908 
1909     const std::shared_ptr<librdf_stream> pStream(
1910         librdf_model_find_statements_in_context(m_pModel.get(),
1911             pStatement.get(), pContext.get()),
1912         safe_librdf_free_stream);
1913     if (!pStream) {
1914         throw rdf::RepositoryException(
1915             "librdf_Repository::removeStatements: "
1916             "librdf_model_find_statements_in_context failed", *this);
1917     }
1918 
1919     if (librdf_stream_end(pStream.get()))
1920         return;
1921 
1922     do {
1923         librdf_statement *pStmt( librdf_stream_get_object(pStream.get()) );
1924         if (!pStmt) {
1925             throw rdf::RepositoryException(
1926                 "librdf_Repository::removeStatements: "
1927                 "librdf_stream_get_object failed", *this);
1928         }
1929         if (librdf_model_context_remove_statement(m_pModel.get(),
1930                 pContext.get(), pStmt)) {
1931             throw rdf::RepositoryException(
1932                 "librdf_Repository::removeStatements: "
1933                 "librdf_model_context_remove_statement failed", *this);
1934         }
1935     } while (!librdf_stream_next(pStream.get()));
1936 }
1937 
1938 std::vector<rdf::Statement>
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)1939 librdf_Repository::getStatementsGraph_NoLock(
1940     const uno::Reference< rdf::XResource > & i_xSubject,
1941     const uno::Reference< rdf::XURI > & i_xPredicate,
1942     const uno::Reference< rdf::XNode > & i_xObject,
1943     const uno::Reference< rdf::XURI > & i_xGraphName,
1944     bool i_Internal)
1945 //throw (uno::RuntimeException, lang::IllegalArgumentException,
1946 //    container::NoSuchElementException, rdf::RepositoryException)
1947 {
1948     std::vector<rdf::Statement> ret;
1949 
1950     // N.B.: if any of subject, predicate, object is an XMetadatable, and
1951     // has no metadata reference, then there cannot be any node in the graph
1952     // representing it; in order to prevent side effect
1953     // (ensureMetadataReference), check for this condition and return
1954     if (isMetadatableWithoutMetadata(i_xSubject)   ||
1955         isMetadatableWithoutMetadata(i_xPredicate) ||
1956         isMetadatableWithoutMetadata(i_xObject))
1957     {
1958         return ret;
1959     }
1960 
1961     librdf_TypeConverter::Statement const stmt(
1962         librdf_TypeConverter::extractStatement_NoLock(
1963             i_xSubject, i_xPredicate, i_xObject));
1964     const OUString contextU( i_xGraphName->getStringValue() );
1965 
1966     ::osl::MutexGuard g(m_aMutex); // don't call i_x* with mutex locked
1967 
1968     if (!i_Internal && (m_NamedGraphs.find(contextU) == m_NamedGraphs.end())) {
1969         throw container::NoSuchElementException(
1970                 "librdf_Repository::getStatements: "
1971                 "no graph with given URI exists", *this);
1972     }
1973     const OString context(
1974         OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) );
1975 
1976     const std::shared_ptr<librdf_node> pContext(
1977         librdf_new_node_from_uri_string(m_pWorld.get(),
1978             reinterpret_cast<const unsigned char*> (context.getStr())),
1979         safe_librdf_free_node);
1980     if (!pContext) {
1981         throw uno::RuntimeException(
1982             "librdf_Repository::getStatements: "
1983             "librdf_new_node_from_uri_string failed", *this);
1984     }
1985     const std::shared_ptr<librdf_statement> pStatement(
1986         librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt),
1987         safe_librdf_free_statement);
1988     OSL_ENSURE(pStatement, "mkStatement failed");
1989 
1990     const std::shared_ptr<librdf_stream> pStream(
1991         librdf_model_find_statements_in_context(m_pModel.get(),
1992             pStatement.get(), pContext.get()),
1993         safe_librdf_free_stream);
1994     if (!pStream) {
1995         throw rdf::RepositoryException(
1996             "librdf_Repository::getStatements: "
1997             "librdf_model_find_statements_in_context failed", *this);
1998     }
1999 
2000     librdf_node *pCtxt1(
2001 #if LIBRDF_VERSION >= 10012
2002         librdf_stream_get_context2(pStream.get()) );
2003 #else
2004         static_cast<librdf_node *>(librdf_stream_get_context(pStream.get())) );
2005 #endif
2006     while (!librdf_stream_end(pStream.get()))
2007     {
2008         auto pCtxt = pCtxt1;
2009         librdf_statement *pStmt( librdf_stream_get_object(pStream.get()) );
2010         if (!pStmt) {
2011             rdf::QueryException e(
2012                 "librdf_GraphResult::nextElement: "
2013                 "librdf_stream_get_object failed", *this);
2014             throw lang::WrappedTargetException(
2015                 "librdf_GraphResult::nextElement: "
2016                 "librdf_stream_get_object failed", *this,
2017                     uno::makeAny(e));
2018         }
2019         // NB: pCtxt may be null here if this is result of a graph query
2020         if (pCtxt && isInternalContext(pCtxt)) {
2021             pCtxt = nullptr; // XML ID context is implementation detail!
2022         }
2023 
2024         ret.emplace_back(
2025             getTypeConverter().convertToStatement(pStmt, pCtxt) );
2026 
2027         // NB: this will invalidate current item.
2028         librdf_stream_next(pStream.get());
2029     }
2030 
2031     return ret;
2032 }
2033 
2034 extern "C"
librdf_raptor_init(void *,raptor_world * pRaptorWorld)2035 void librdf_raptor_init(void* /*user_data*/, raptor_world* pRaptorWorld)
2036 {
2037     // fdo#64672 prevent raptor from setting global libxml2 error handlers
2038     raptor_world_set_flag(pRaptorWorld,
2039             RAPTOR_WORLD_FLAG_LIBXML_STRUCTURED_ERROR_SAVE, 0);
2040     raptor_world_set_flag(pRaptorWorld,
2041             RAPTOR_WORLD_FLAG_LIBXML_GENERIC_ERROR_SAVE, 0);
2042 }
2043 
createWorld_Lock() const2044 librdf_world *librdf_TypeConverter::createWorld_Lock() const
2045 {
2046     // create and initialize world
2047     librdf_world *pWorld( librdf_new_world() );
2048     if (!pWorld) {
2049         throw uno::RuntimeException(
2050             "librdf_TypeConverter::createWorld: librdf_new_world failed",
2051             m_rRep);
2052     }
2053     librdf_world_set_raptor_init_handler(pWorld, nullptr, &librdf_raptor_init);
2054     //FIXME logger, digest, features?
2055     xsltSecurityPrefsPtr origprefs = xsltGetDefaultSecurityPrefs();
2056     librdf_world_open(pWorld);
2057     xsltSecurityPrefsPtr newprefs = xsltGetDefaultSecurityPrefs();
2058     if (newprefs != origprefs) {
2059         // #i110523# restore libxslt global configuration
2060         // (gratuitously overwritten by raptor_init_parser_grddl_common)
2061         // (this is the only reason unordf is linked against libxslt)
2062         xsltSetDefaultSecurityPrefs(origprefs);
2063     }
2064     return pWorld;
2065 }
2066 
2067 librdf_storage *
createStorage_Lock(librdf_world * i_pWorld) const2068 librdf_TypeConverter::createStorage_Lock(librdf_world *i_pWorld) const
2069 {
2070     librdf_storage *pStorage(
2071 //        librdf_new_storage(i_pWorld, "memory", NULL, "contexts='yes'") );
2072         librdf_new_storage(i_pWorld, "hashes", nullptr,
2073             "contexts='yes',hash-type='memory'") );
2074     if (!pStorage) {
2075         throw uno::RuntimeException(
2076             "librdf_TypeConverter::createStorage: librdf_new_storage failed",
2077             m_rRep);
2078     }
2079     return pStorage;
2080 }
2081 
createModel_Lock(librdf_world * i_pWorld,librdf_storage * i_pStorage) const2082 librdf_model *librdf_TypeConverter::createModel_Lock(
2083     librdf_world *i_pWorld, librdf_storage * i_pStorage) const
2084 {
2085     librdf_model *pRepository( librdf_new_model(i_pWorld, i_pStorage, nullptr) );
2086     if (!pRepository) {
2087         throw uno::RuntimeException(
2088             "librdf_TypeConverter::createModel: librdf_new_model failed",
2089             m_rRep);
2090     }
2091     //FIXME
2092 #if 0
2093     {
2094         librdf_uri * ctxt = librdf_new_uri(i_pWorld, reinterpret_cast<const unsigned char *>(LIBRDF_MODEL_FEATURE_CONTEXTS));
2095         librdf_node * contexts = librdf_model_get_feature(repository, ctxt);
2096         if (!contexts)
2097             throw;
2098         std::cout << "value of contexts feature: ";
2099         prtNode(contexts);
2100         std::cout << std::endl;
2101         // librdf_model_set_feature(repository, LIBRDF_FEATURE_CONTEXTS, ...);
2102         safe_librdf_free_node(contexts);
2103         safe_librdf_free_uri(ctxt);
2104     }
2105 #endif
2106     return pRepository;
2107 }
2108 
2109 // this does NOT create a node, only URI
mkURI_Lock(librdf_world * i_pWorld,OString const & i_rURI)2110 librdf_uri* librdf_TypeConverter::mkURI_Lock( librdf_world* i_pWorld,
2111     OString const& i_rURI)
2112 {
2113     librdf_uri *pURI( librdf_new_uri(i_pWorld,
2114         reinterpret_cast<const unsigned char *>(i_rURI.getStr())));
2115     if (!pURI) {
2116         throw uno::RuntimeException(
2117             "librdf_TypeConverter::mkURI: librdf_new_uri failed", nullptr);
2118     }
2119     return pURI;
2120 }
2121 
2122 // extract blank or URI node - call without Mutex locked
2123 std::shared_ptr<librdf_TypeConverter::Resource>
extractResource_NoLock(const uno::Reference<rdf::XResource> & i_xResource)2124 librdf_TypeConverter::extractResource_NoLock(
2125     const uno::Reference< rdf::XResource > & i_xResource)
2126 {
2127     if (!i_xResource.is()) {
2128         return std::shared_ptr<Resource>();
2129     }
2130     uno::Reference< rdf::XBlankNode > xBlankNode(i_xResource, uno::UNO_QUERY);
2131     if (xBlankNode.is()) {
2132         const OString label(
2133             OUStringToOString(xBlankNode->getStringValue(),
2134             RTL_TEXTENCODING_UTF8) );
2135         return std::make_shared<BlankNode>(label);
2136     } else { // assumption: everything else is URI
2137         const OString uri(
2138             OUStringToOString(i_xResource->getStringValue(),
2139             RTL_TEXTENCODING_UTF8) );
2140         return std::make_shared<URI>(uri);
2141     }
2142 }
2143 
2144 void
extractResourceToCacheKey_NoLock(const uno::Reference<rdf::XResource> & i_xResource,OUStringBuffer & rBuffer)2145 librdf_TypeConverter::extractResourceToCacheKey_NoLock(
2146     const uno::Reference< rdf::XResource > & i_xResource, OUStringBuffer& rBuffer)
2147 {
2148     if (!i_xResource.is()) {
2149         return;
2150     }
2151     uno::Reference< rdf::XBlankNode > xBlankNode(i_xResource, uno::UNO_QUERY);
2152     if (xBlankNode.is()) {
2153         rBuffer.append("BlankNode " + xBlankNode->getStringValue());
2154     } else { // assumption: everything else is URI
2155         rBuffer.append("URI " + i_xResource->getStringValue());
2156     }
2157 }
2158 
2159 // create blank or URI node
mkResource_Lock(librdf_world * i_pWorld,Resource const * const i_pResource)2160 librdf_node* librdf_TypeConverter::mkResource_Lock( librdf_world* i_pWorld,
2161     Resource const*const i_pResource)
2162 {
2163     if (!i_pResource) return nullptr;
2164     BlankNode const*const pBlankNode(
2165             dynamic_cast<BlankNode const*>(i_pResource));
2166     if (pBlankNode) {
2167         librdf_node *pNode(
2168             librdf_new_node_from_blank_identifier(i_pWorld,
2169                 reinterpret_cast<const unsigned char*>(
2170                     pBlankNode->value.getStr())));
2171         if (!pNode) {
2172             throw uno::RuntimeException(
2173                 "librdf_TypeConverter::mkResource: "
2174                 "librdf_new_node_from_blank_identifier failed", nullptr);
2175         }
2176         return pNode;
2177     } else { // assumption: everything else is URI
2178         URI const*const pURI(dynamic_cast<URI const*>(i_pResource));
2179         assert(pURI);
2180         librdf_node *pNode(
2181             librdf_new_node_from_uri_string(i_pWorld,
2182                 reinterpret_cast<const unsigned char*>(pURI->value.getStr())));
2183         if (!pNode) {
2184             throw uno::RuntimeException(
2185                 "librdf_TypeConverter::mkResource: "
2186                 "librdf_new_node_from_uri_string failed", nullptr);
2187         }
2188         return pNode;
2189     }
2190 }
2191 
2192 // extract blank or URI or literal node - call without Mutex locked
2193 std::shared_ptr<librdf_TypeConverter::Node>
extractNode_NoLock(const uno::Reference<rdf::XNode> & i_xNode)2194 librdf_TypeConverter::extractNode_NoLock(
2195     const uno::Reference< rdf::XNode > & i_xNode)
2196 {
2197     if (!i_xNode.is()) {
2198         return std::shared_ptr<Node>();
2199     }
2200     uno::Reference< rdf::XResource > xResource(i_xNode, uno::UNO_QUERY);
2201     if (xResource.is()) {
2202         return extractResource_NoLock(xResource);
2203     }
2204     uno::Reference< rdf::XLiteral> xLiteral(i_xNode, uno::UNO_QUERY);
2205     OSL_ENSURE(xLiteral.is(),
2206         "mkNode: someone invented a new rdf.XNode and did not tell me");
2207     if (!xLiteral.is()) {
2208         return std::shared_ptr<Node>();
2209     }
2210     const OString val(
2211         OUStringToOString(xLiteral->getValue(),
2212         RTL_TEXTENCODING_UTF8) );
2213     const OString lang(
2214         OUStringToOString(xLiteral->getLanguage(),
2215         RTL_TEXTENCODING_UTF8) );
2216     const uno::Reference< rdf::XURI > xType(xLiteral->getDatatype());
2217     std::optional<OString> type;
2218     if (xType.is())
2219     {
2220         type =
2221             OUStringToOString(xType->getStringValue(), RTL_TEXTENCODING_UTF8);
2222     }
2223     return std::make_shared<Literal>(val, lang, type);
2224 }
2225 
2226 // extract blank or URI or literal node - call without Mutex locked
2227 void
extractNodeToCacheKey_NoLock(const uno::Reference<rdf::XNode> & i_xNode,OUStringBuffer & rBuffer)2228 librdf_TypeConverter::extractNodeToCacheKey_NoLock(
2229     const uno::Reference< rdf::XNode > & i_xNode,
2230     OUStringBuffer& rBuffer)
2231 {
2232     if (!i_xNode.is()) {
2233         return;
2234     }
2235     uno::Reference< rdf::XResource > xResource(i_xNode, uno::UNO_QUERY);
2236     if (xResource.is()) {
2237         return extractResourceToCacheKey_NoLock(xResource, rBuffer);
2238     }
2239     uno::Reference< rdf::XLiteral> xLiteral(i_xNode, uno::UNO_QUERY);
2240     OSL_ENSURE(xLiteral.is(),
2241         "mkNode: someone invented a new rdf.XNode and did not tell me");
2242     if (!xLiteral.is()) {
2243         return;
2244     }
2245     rBuffer.append("Literal " + xLiteral->getValue() + "\t" + xLiteral->getLanguage());
2246     const uno::Reference< rdf::XURI > xType(xLiteral->getDatatype());
2247     if (xType.is())
2248         rBuffer.append("\t" + xType->getStringValue());
2249 }
2250 
2251 // create blank or URI or literal node
mkNode_Lock(librdf_world * i_pWorld,Node const * const i_pNode)2252 librdf_node* librdf_TypeConverter::mkNode_Lock( librdf_world* i_pWorld,
2253     Node const*const i_pNode)
2254 {
2255     if (!i_pNode) return nullptr;
2256     Resource const*const pResource(dynamic_cast<Resource const*>(i_pNode));
2257     if (pResource) {
2258         return mkResource_Lock(i_pWorld, pResource);
2259     }
2260 
2261     Literal const*const pLiteral(dynamic_cast<Literal const*>(i_pNode));
2262     assert(pLiteral);
2263     librdf_node * ret(nullptr);
2264     if (pLiteral->language.isEmpty()) {
2265         if (!pLiteral->type) {
2266             ret = librdf_new_node_from_literal(i_pWorld,
2267                 reinterpret_cast<const unsigned char*>(pLiteral->value.getStr())
2268                 , nullptr, 0);
2269         } else {
2270             const std::shared_ptr<librdf_uri> pDatatype(
2271                 mkURI_Lock(i_pWorld, *pLiteral->type),
2272                 safe_librdf_free_uri);
2273             ret = librdf_new_node_from_typed_literal(i_pWorld,
2274                 reinterpret_cast<const unsigned char*>(pLiteral->value.getStr())
2275                 , nullptr, pDatatype.get());
2276         }
2277     } else {
2278         if (!pLiteral->type) {
2279             ret = librdf_new_node_from_literal(i_pWorld,
2280                 reinterpret_cast<const unsigned char*>(pLiteral->value.getStr())
2281                 , pLiteral->language.getStr(), 0);
2282         } else {
2283             OSL_FAIL("mkNode: invalid literal");
2284             return nullptr;
2285         }
2286     }
2287     if (!ret) {
2288         throw uno::RuntimeException(
2289             "librdf_TypeConverter::mkNode: librdf_new_node_from_literal failed", nullptr);
2290     }
2291     return ret;
2292 }
2293 
2294 // extract statement - call without Mutex locked
extractStatement_NoLock(const uno::Reference<rdf::XResource> & i_xSubject,const uno::Reference<rdf::XURI> & i_xPredicate,const uno::Reference<rdf::XNode> & i_xObject)2295 librdf_TypeConverter::Statement librdf_TypeConverter::extractStatement_NoLock(
2296     const uno::Reference< rdf::XResource > & i_xSubject,
2297     const uno::Reference< rdf::XURI > & i_xPredicate,
2298     const uno::Reference< rdf::XNode > & i_xObject)
2299 {
2300     std::shared_ptr<Resource> const pSubject(
2301             extractResource_NoLock(i_xSubject));
2302     std::shared_ptr<URI> const pPredicate(
2303         std::dynamic_pointer_cast<URI>(extractResource_NoLock(i_xPredicate)));
2304     std::shared_ptr<Node> const pObject(extractNode_NoLock(i_xObject));
2305     return Statement(pSubject, pPredicate, pObject);
2306 }
2307 
mkStatement_Lock(librdf_world * i_pWorld,Statement const & i_rStatement)2308 librdf_statement* librdf_TypeConverter::mkStatement_Lock(librdf_world* i_pWorld,
2309     Statement const& i_rStatement)
2310 {
2311     librdf_node *const pSubject(
2312             mkResource_Lock(i_pWorld, i_rStatement.pSubject.get()) );
2313     librdf_node* pPredicate(nullptr);
2314     librdf_node* pObject(nullptr);
2315     try {
2316         pPredicate = mkResource_Lock(i_pWorld, i_rStatement.pPredicate.get());
2317         try {
2318             pObject = mkNode_Lock(i_pWorld, i_rStatement.pObject.get());
2319         } catch (...) {
2320             safe_librdf_free_node(pPredicate);
2321             throw;
2322         }
2323     } catch (...) {
2324         safe_librdf_free_node(pSubject);
2325         throw;
2326     }
2327     // NB: this takes ownership of the nodes! (which is really ugly)
2328     librdf_statement* pStatement( librdf_new_statement_from_nodes(i_pWorld,
2329         pSubject, pPredicate, pObject) );
2330     if (!pStatement) {
2331         throw uno::RuntimeException(
2332             "librdf_TypeConverter::mkStatement: "
2333             "librdf_new_statement_from_nodes failed", nullptr);
2334     }
2335     return pStatement;
2336 }
2337 
2338 uno::Reference<rdf::XURI>
convertToXURI(librdf_uri * i_pURI) const2339 librdf_TypeConverter::convertToXURI(librdf_uri* i_pURI) const
2340 {
2341     if (!i_pURI) return nullptr;
2342     const unsigned char* uri( librdf_uri_as_string(i_pURI) );
2343     if (!uri) {
2344         throw uno::RuntimeException(
2345             "librdf_TypeConverter::convertToXURI: "
2346             "librdf_uri_as_string failed", m_rRep);
2347     }
2348     OUString uriU( OStringToOUString(
2349         std::string_view(reinterpret_cast<const char*>(uri)),
2350         RTL_TEXTENCODING_UTF8) );
2351     try {
2352         return rdf::URI::create(m_xContext, uriU);
2353     } catch (const lang::IllegalArgumentException &) {
2354         css::uno::Any anyEx = cppu::getCaughtException();
2355         throw lang::WrappedTargetRuntimeException(
2356                 "librdf_TypeConverter::convertToXURI: "
2357                 "illegal uri", m_rRep, anyEx);
2358     }
2359 }
2360 
2361 uno::Reference<rdf::XURI>
convertToXURI(librdf_node * i_pNode) const2362 librdf_TypeConverter::convertToXURI(librdf_node* i_pNode) const
2363 {
2364     if (!i_pNode) return nullptr;
2365     if (librdf_node_is_resource(i_pNode)) {
2366         librdf_uri* pURI( librdf_node_get_uri(i_pNode) );
2367         if (!pURI) {
2368             throw uno::RuntimeException(
2369                 "librdf_TypeConverter::convertToXURI: "
2370                 "resource has no uri", m_rRep);
2371         }
2372         return convertToXURI(pURI);
2373     } else {
2374         OSL_FAIL("convertToXURI: unknown librdf_node");
2375         return nullptr;
2376     }
2377 }
2378 
2379 uno::Reference<rdf::XResource>
convertToXResource(librdf_node * i_pNode) const2380 librdf_TypeConverter::convertToXResource(librdf_node* i_pNode) const
2381 {
2382     if (!i_pNode) return nullptr;
2383     if (librdf_node_is_blank(i_pNode)) {
2384         const unsigned char* label( librdf_node_get_blank_identifier(i_pNode) );
2385         if (!label) {
2386             throw uno::RuntimeException(
2387                 "librdf_TypeConverter::convertToXResource: "
2388                 "blank node has no label", m_rRep);
2389         }
2390         OUString labelU( OStringToOUString(
2391             std::string_view(reinterpret_cast<const char*>(label)),
2392             RTL_TEXTENCODING_UTF8) );
2393         try {
2394             return rdf::BlankNode::create(m_xContext, labelU);
2395         } catch (const lang::IllegalArgumentException &) {
2396             css::uno::Any anyEx = cppu::getCaughtException();
2397             throw lang::WrappedTargetRuntimeException(
2398                     "librdf_TypeConverter::convertToXResource: "
2399                     "illegal blank node label", m_rRep, anyEx);
2400         }
2401     } else {
2402         return convertToXURI(i_pNode);
2403     }
2404 }
2405 
2406 uno::Reference<rdf::XNode>
convertToXNode(librdf_node * i_pNode) const2407 librdf_TypeConverter::convertToXNode(librdf_node* i_pNode) const
2408 {
2409     if (!i_pNode) return nullptr;
2410     if (!librdf_node_is_literal(i_pNode)) {
2411         return convertToXResource(i_pNode);
2412     }
2413     const unsigned char* value( librdf_node_get_literal_value(i_pNode) );
2414     if (!value) {
2415         throw uno::RuntimeException(
2416             "librdf_TypeConverter::convertToXNode: "
2417             "literal has no value", m_rRep);
2418     }
2419     const char * lang( librdf_node_get_literal_value_language(i_pNode) );
2420     librdf_uri* pType(
2421         librdf_node_get_literal_value_datatype_uri(i_pNode) );
2422     OSL_ENSURE(!lang || !pType, "convertToXNode: invalid literal");
2423     const OUString valueU( OStringToOUString(
2424         std::string_view(reinterpret_cast<const char*>(value)),
2425         RTL_TEXTENCODING_UTF8) );
2426     if (lang) {
2427         const OUString langU( OStringToOUString(
2428             std::string_view(reinterpret_cast<const char*>(lang)),
2429             RTL_TEXTENCODING_UTF8) );
2430         return rdf::Literal::createWithLanguage(m_xContext, valueU, langU);
2431     } else if (pType) {
2432         uno::Reference<rdf::XURI> xType(convertToXURI(pType));
2433         OSL_ENSURE(xType.is(), "convertToXNode: null uri");
2434         return rdf::Literal::createWithType(m_xContext, valueU, xType);
2435     } else {
2436         return rdf::Literal::create(m_xContext, valueU);
2437     }
2438 }
2439 
2440 rdf::Statement
convertToStatement(librdf_statement * i_pStmt,librdf_node * i_pContext) const2441 librdf_TypeConverter::convertToStatement(librdf_statement* i_pStmt,
2442     librdf_node* i_pContext) const
2443 {
2444     if (!i_pStmt) {
2445         throw uno::RuntimeException();
2446     }
2447     return rdf::Statement(
2448         convertToXResource(librdf_statement_get_subject(i_pStmt)),
2449         convertToXURI(librdf_statement_get_predicate(i_pStmt)),
2450         convertToXNode(librdf_statement_get_object(i_pStmt)),
2451         convertToXURI(i_pContext));
2452 }
2453 
2454 } // closing anonymous implementation namespace
2455 
2456 
2457 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
unoxml_rdfRepository_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2458 unoxml_rdfRepository_get_implementation(
2459     css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
2460 {
2461     return cppu::acquire(new librdf_Repository(context));
2462 }
2463 
2464 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2465