Line data Source code
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 :
21 : #include "RDFaImportHelper.hxx"
22 :
23 : #include <xmloff/xmlimp.hxx>
24 : #include <xmloff/nmspmap.hxx>
25 :
26 : #include <comphelper/sequenceasvector.hxx>
27 :
28 : #include <com/sun/star/rdf/URI.hpp>
29 : #include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
30 : #include <com/sun/star/rdf/XDocumentRepository.hpp>
31 :
32 : #include <rtl/ustring.hxx>
33 :
34 : #include <boost/bind.hpp>
35 : #include <boost/iterator_adaptors.hpp>
36 : #ifndef BOOST_ITERATOR_ADAPTOR_DWA053000_HPP_ // from iterator_adaptors.hpp
37 : // N.B.: the check for the header guard _of a specific version of boost_
38 : // is here so this may work on different versions of boost,
39 : // which sadly put the goods in different header files
40 : #include <boost/iterator/transform_iterator.hpp>
41 : #endif
42 :
43 : #include <map>
44 : #include <iterator>
45 : #include <functional>
46 : #include <algorithm>
47 :
48 :
49 : using namespace ::com::sun::star;
50 :
51 : namespace xmloff {
52 :
53 : /** a bit of context for parsing RDFa attributes */
54 : class SAL_DLLPRIVATE RDFaReader
55 : {
56 : const SvXMLImport & m_rImport;
57 :
58 0 : const SvXMLImport & GetImport() const { return m_rImport; }
59 :
60 : //FIXME: this is an ugly hack to workaround buggy SvXMLImport::GetAbsolute
61 0 : ::rtl::OUString GetAbsoluteReference(::rtl::OUString const & i_rURI) const
62 : {
63 0 : if (i_rURI.isEmpty() || i_rURI[0] == '#')
64 : {
65 0 : return GetImport().GetBaseURL() + i_rURI;
66 : }
67 : else
68 : {
69 0 : return GetImport().GetAbsoluteReference(i_rURI);
70 : }
71 : }
72 :
73 : public:
74 0 : RDFaReader(SvXMLImport const & i_rImport)
75 0 : : m_rImport(i_rImport)
76 0 : { }
77 :
78 : // returns URI or blank node!
79 : ::rtl::OUString ReadCURIE(::rtl::OUString const & i_rCURIE) const;
80 :
81 : std::vector< ::rtl::OUString >
82 : ReadCURIEs(::rtl::OUString const & i_rCURIEs) const;
83 :
84 : ::rtl::OUString
85 : ReadURIOrSafeCURIE( ::rtl::OUString const & i_rURIOrSafeCURIE) const;
86 : };
87 :
88 : /** helper to insert RDFa statements into the RDF repository */
89 0 : class SAL_DLLPRIVATE RDFaInserter
90 : {
91 : const uno::Reference<uno::XComponentContext> m_xContext;
92 : uno::Reference< rdf::XDocumentRepository > m_xRepository;
93 :
94 : typedef ::std::map< ::rtl::OUString, uno::Reference< rdf::XBlankNode > >
95 : BlankNodeMap_t;
96 :
97 : BlankNodeMap_t m_BlankNodeMap;
98 :
99 : public:
100 0 : RDFaInserter(uno::Reference<uno::XComponentContext> const & i_xContext,
101 : uno::Reference< rdf::XDocumentRepository > const & i_xRepository)
102 : : m_xContext(i_xContext)
103 0 : , m_xRepository(i_xRepository)
104 0 : {}
105 :
106 : uno::Reference< rdf::XBlankNode >
107 : LookupBlankNode(::rtl::OUString const & i_rNodeId );
108 :
109 : uno::Reference< rdf::XURI >
110 : MakeURI( ::rtl::OUString const & i_rURI) const;
111 :
112 : uno::Reference< rdf::XResource>
113 : MakeResource( ::rtl::OUString const & i_rResource);
114 :
115 : void InsertRDFaEntry(struct RDFaEntry const & i_rEntry);
116 : };
117 :
118 : /** store parsed RDFa attributes */
119 0 : struct SAL_DLLPRIVATE ParsedRDFaAttributes
120 : {
121 : ::rtl::OUString m_About;
122 : ::std::vector< ::rtl::OUString > m_Properties;
123 : ::rtl::OUString m_Content;
124 : ::rtl::OUString m_Datatype;
125 :
126 0 : ParsedRDFaAttributes(
127 : ::rtl::OUString const & i_rAbout,
128 : ::std::vector< ::rtl::OUString > const & i_rProperties,
129 : ::rtl::OUString const & i_rContent,
130 : ::rtl::OUString const & i_rDatatype)
131 : : m_About(i_rAbout)
132 : , m_Properties(i_rProperties)
133 : , m_Content(i_rContent)
134 0 : , m_Datatype(i_rDatatype)
135 0 : { }
136 : };
137 :
138 : /** store metadatable object and its RDFa attributes */
139 0 : struct SAL_DLLPRIVATE RDFaEntry
140 : {
141 : uno::Reference<rdf::XMetadatable> m_xObject;
142 : ::boost::shared_ptr<ParsedRDFaAttributes> m_pRDFaAttributes;
143 :
144 0 : RDFaEntry(uno::Reference<rdf::XMetadatable> const & i_xObject,
145 : ::boost::shared_ptr<ParsedRDFaAttributes> const& i_pRDFaAttributes)
146 : : m_xObject(i_xObject)
147 0 : , m_pRDFaAttributes(i_pRDFaAttributes)
148 0 : { }
149 : };
150 :
151 : ////////////////////////////////////////////////////////////////////////////
152 :
153 :
154 0 : static inline bool isWS(const sal_Unicode i_Char)
155 : {
156 : return ('\t' == i_Char) || ('\n' == i_Char) || ('\r' == i_Char)
157 0 : || (' ' == i_Char);
158 : }
159 :
160 0 : static ::rtl::OUString splitAtWS(::rtl::OUString & io_rString)
161 : {
162 0 : const sal_Int32 len( io_rString.getLength() );
163 0 : sal_Int32 idxstt(0);
164 0 : while ((idxstt < len) && ( isWS(io_rString[idxstt])))
165 0 : ++idxstt; // skip leading ws
166 0 : sal_Int32 idxend(idxstt);
167 0 : while ((idxend < len) && (!isWS(io_rString[idxend])))
168 0 : ++idxend; // the CURIE
169 0 : const ::rtl::OUString ret(io_rString.copy(idxstt, idxend - idxstt));
170 0 : io_rString = io_rString.copy(idxend); // rest
171 0 : return ret;
172 : }
173 :
174 : ::rtl::OUString
175 0 : RDFaReader::ReadCURIE(::rtl::OUString const & i_rCURIE) const
176 : {
177 : // the RDFa spec says that a prefix is required (it may be empty: ":foo")
178 0 : const sal_Int32 idx( i_rCURIE.indexOf(':') );
179 0 : if (idx >= 0)
180 : {
181 0 : ::rtl::OUString Prefix;
182 0 : ::rtl::OUString LocalName;
183 0 : ::rtl::OUString Namespace;
184 0 : sal_uInt16 nKey( GetImport().GetNamespaceMap()._GetKeyByAttrName(
185 0 : i_rCURIE, &Prefix, &LocalName, &Namespace) );
186 0 : if ( Prefix == "_" )
187 : {
188 : // eeek, it's a bnode!
189 : // "_" is not a valid URI scheme => we can identify bnodes
190 0 : return i_rCURIE;
191 : }
192 : else
193 : {
194 : OSL_ENSURE(XML_NAMESPACE_NONE != nKey, "no namespace?");
195 0 : if ((XML_NAMESPACE_UNKNOWN != nKey) &&
196 : (XML_NAMESPACE_XMLNS != nKey))
197 : {
198 : // N.B.: empty LocalName is valid!
199 0 : const ::rtl::OUString URI(Namespace + LocalName);
200 0 : return GetAbsoluteReference(URI);
201 : }
202 : else
203 : {
204 : OSL_TRACE( "ReadCURIE: invalid CURIE: invalid prefix" );
205 0 : return ::rtl::OUString();
206 : }
207 0 : }
208 : }
209 : OSL_TRACE( "ReadCURIE: invalid CURIE: no prefix" );
210 0 : return ::rtl::OUString();
211 : }
212 :
213 : ::std::vector< ::rtl::OUString >
214 0 : RDFaReader::ReadCURIEs(::rtl::OUString const & i_rCURIEs) const
215 : {
216 0 : std::vector< ::rtl::OUString > vec;
217 0 : ::rtl::OUString CURIEs(i_rCURIEs);
218 0 : do {
219 0 : ::rtl::OUString curie( splitAtWS(CURIEs) );
220 0 : if (!curie.isEmpty())
221 : {
222 0 : const ::rtl::OUString uri(ReadCURIE(curie));
223 0 : if (!uri.isEmpty())
224 : {
225 0 : vec.push_back(uri);
226 0 : }
227 0 : }
228 : }
229 0 : while (!CURIEs.isEmpty());
230 0 : if (vec.empty())
231 : {
232 : OSL_TRACE( "ReadCURIEs: invalid CURIEs" );
233 : }
234 0 : return vec;
235 : }
236 :
237 : ::rtl::OUString
238 0 : RDFaReader::ReadURIOrSafeCURIE(::rtl::OUString const & i_rURIOrSafeCURIE) const
239 : {
240 0 : const sal_Int32 len(i_rURIOrSafeCURIE.getLength());
241 0 : if (len && (i_rURIOrSafeCURIE[0] == '['))
242 : {
243 0 : if ((len >= 2) && (i_rURIOrSafeCURIE[len - 1] == ']'))
244 : {
245 0 : return ReadCURIE(i_rURIOrSafeCURIE.copy(1, len - 2));
246 : }
247 : else
248 : {
249 : OSL_TRACE( "ReadURIOrSafeCURIE: invalid SafeCURIE" );
250 0 : return ::rtl::OUString();
251 : }
252 : }
253 : else
254 : {
255 0 : if (i_rURIOrSafeCURIE.matchAsciiL("_:", 2)) // blank node
256 : {
257 : OSL_TRACE( "ReadURIOrSafeCURIE: invalid URI: scheme is _" );
258 0 : return ::rtl::OUString();
259 : }
260 : else
261 : {
262 0 : return GetAbsoluteReference(i_rURIOrSafeCURIE);
263 : }
264 : }
265 : }
266 :
267 : ////////////////////////////////////////////////////////////////////////////
268 :
269 : uno::Reference< rdf::XBlankNode >
270 0 : RDFaInserter::LookupBlankNode(::rtl::OUString const & i_rNodeId )
271 : {
272 0 : uno::Reference< rdf::XBlankNode > & rEntry( m_BlankNodeMap[ i_rNodeId ] );
273 0 : if (!rEntry.is())
274 : {
275 0 : rEntry = m_xRepository->createBlankNode();
276 : }
277 0 : return rEntry;
278 : }
279 :
280 : uno::Reference< rdf::XURI >
281 0 : RDFaInserter::MakeURI( ::rtl::OUString const & i_rURI) const
282 : {
283 0 : if (i_rURI.matchAsciiL("_:", 2)) // blank node
284 : {
285 : OSL_TRACE("MakeURI: cannot create URI for blank node");
286 0 : return 0;
287 : }
288 : else
289 : {
290 : try
291 : {
292 0 : return rdf::URI::create( m_xContext, i_rURI );
293 : }
294 0 : catch (uno::Exception &)
295 : {
296 : OSL_FAIL("MakeURI: cannot create URI");
297 0 : return 0;
298 : }
299 : }
300 : }
301 :
302 : uno::Reference< rdf::XResource>
303 0 : RDFaInserter::MakeResource( ::rtl::OUString const & i_rResource)
304 : {
305 0 : if (i_rResource.matchAsciiL("_:", 2)) // blank node
306 : {
307 : // we cannot use the blank node label as-is: it must be distinct
308 : // from labels in other graphs, so create fresh ones per XML stream
309 : // N.B.: content.xml and styles.xml are distinct graphs
310 0 : ::rtl::OUString name( i_rResource.copy(2) );
311 0 : const uno::Reference< rdf::XBlankNode > xBNode( LookupBlankNode(name) );
312 : OSL_ENSURE(xBNode.is(), "no blank node?");
313 0 : return uno::Reference<rdf::XResource>( xBNode, uno::UNO_QUERY);
314 : }
315 : else
316 : {
317 : return uno::Reference<rdf::XResource>( MakeURI( i_rResource ),
318 0 : uno::UNO_QUERY);
319 : }
320 : }
321 :
322 : /** i wrote this because c++ implementations cannot agree on which variant
323 : of boost::bind and std::mem_fun_ref applied to Reference::is compiles */
324 : class ref_is_null :
325 : public ::std::unary_function<sal_Bool, const uno::Reference<rdf::XURI> & >
326 : {
327 : public:
328 0 : sal_Bool operator() (const uno::Reference<rdf::XURI> & i_rRef)
329 : {
330 0 : return !i_rRef.is();
331 : }
332 : };
333 :
334 0 : void RDFaInserter::InsertRDFaEntry(
335 : struct RDFaEntry const & i_rEntry)
336 : {
337 : OSL_ENSURE(i_rEntry.m_xObject.is(),
338 : "InsertRDFaEntry: invalid arg: null object");
339 0 : if (!i_rEntry.m_xObject.is()) return;
340 :
341 : const uno::Reference< rdf::XResource > xSubject(
342 0 : MakeResource( i_rEntry.m_pRDFaAttributes->m_About ) );
343 0 : if (!xSubject.is())
344 : {
345 : return; // invalid
346 : }
347 :
348 0 : ::comphelper::SequenceAsVector< uno::Reference< rdf::XURI > > predicates;
349 :
350 0 : predicates.reserve(i_rEntry.m_pRDFaAttributes->m_Properties.size());
351 :
352 : ::std::remove_copy_if(
353 : ::boost::make_transform_iterator(
354 0 : i_rEntry.m_pRDFaAttributes->m_Properties.begin(),
355 : ::boost::bind(&RDFaInserter::MakeURI, this, _1)),
356 : // argh, this must be the same type :(
357 : ::boost::make_transform_iterator(
358 0 : i_rEntry.m_pRDFaAttributes->m_Properties.end(),
359 : ::boost::bind(&RDFaInserter::MakeURI, this, _1)),
360 : ::std::back_inserter(predicates),
361 0 : ref_is_null() );
362 : // compiles only on wntmsci12
363 : // ::boost::bind( ::std::logical_not<sal_Bool>(), ::boost::bind<sal_Bool>(&uno::Reference<rdf::XURI>::is, _1)));
364 : // compiles on unxsoli4, wntsci12, but not unxlngi6
365 : // ::boost::bind( ::std::logical_not<sal_Bool>(), ::boost::bind<sal_Bool, com::sun::star::uno::Reference<rdf::XURI> >(&uno::Reference<rdf::XURI>::is, _1)));
366 : // compiles on unxsoli4, unxlngi6, but not wntsci12
367 : // ::std::not1( ::std::mem_fun_ref(&uno::Reference<rdf::XURI>::is)) );
368 :
369 0 : if (!predicates.size())
370 : {
371 : return; // invalid
372 : }
373 :
374 0 : uno::Reference<rdf::XURI> xDatatype;
375 0 : if (!i_rEntry.m_pRDFaAttributes->m_Datatype.isEmpty())
376 : {
377 0 : xDatatype = MakeURI( i_rEntry.m_pRDFaAttributes->m_Datatype );
378 : }
379 :
380 : try
381 : {
382 : // N.B.: this will call xMeta->ensureMetadataReference, which is why
383 : // this must be done _after_ importing the whole XML file,
384 : // to prevent collision between generated ids and ids in the file
385 0 : m_xRepository->setStatementRDFa(xSubject, predicates.getAsConstList(),
386 : i_rEntry.m_xObject,
387 0 : i_rEntry.m_pRDFaAttributes->m_Content, xDatatype);
388 : }
389 0 : catch (uno::Exception &)
390 : {
391 : OSL_FAIL("InsertRDFaEntry: setStatementRDFa failed?");
392 0 : }
393 : }
394 :
395 : ////////////////////////////////////////////////////////////////////////////
396 :
397 0 : RDFaImportHelper::RDFaImportHelper(const SvXMLImport & i_rImport)
398 0 : : m_rImport(i_rImport)
399 : {
400 0 : }
401 :
402 0 : RDFaImportHelper::~RDFaImportHelper()
403 : {
404 0 : }
405 :
406 : ::boost::shared_ptr<ParsedRDFaAttributes>
407 0 : RDFaImportHelper::ParseRDFa(
408 : ::rtl::OUString const & i_rAbout,
409 : ::rtl::OUString const & i_rProperty,
410 : ::rtl::OUString const & i_rContent,
411 : ::rtl::OUString const & i_rDatatype)
412 : {
413 0 : if (i_rProperty.isEmpty())
414 : {
415 : OSL_TRACE("AddRDFa: invalid input: xhtml:property empty");
416 0 : return ::boost::shared_ptr<ParsedRDFaAttributes>();
417 : }
418 : // must parse CURIEs here: need namespace declaration context
419 0 : RDFaReader reader(GetImport());
420 0 : const ::rtl::OUString about( reader.ReadURIOrSafeCURIE(i_rAbout) );
421 0 : if (about.isEmpty()) {
422 0 : return ::boost::shared_ptr<ParsedRDFaAttributes>();
423 : }
424 : const ::std::vector< ::rtl::OUString > properties(
425 0 : reader.ReadCURIEs(i_rProperty) );
426 0 : if (!properties.size()) {
427 0 : return ::boost::shared_ptr<ParsedRDFaAttributes>();
428 : }
429 0 : const ::rtl::OUString datatype( !i_rDatatype.isEmpty()
430 : ? reader.ReadCURIE(i_rDatatype)
431 0 : : ::rtl::OUString() );
432 : return ::boost::shared_ptr<ParsedRDFaAttributes>(
433 0 : new ParsedRDFaAttributes(about, properties, i_rContent, datatype));
434 : }
435 :
436 : void
437 0 : RDFaImportHelper::AddRDFa(
438 : uno::Reference<rdf::XMetadatable> const & i_xObject,
439 : ::boost::shared_ptr<ParsedRDFaAttributes> & i_pRDFaAttributes)
440 : {
441 0 : if (!i_xObject.is())
442 : {
443 : OSL_FAIL("AddRDFa: invalid arg: null textcontent");
444 0 : return;
445 : }
446 0 : if (!i_pRDFaAttributes.get())
447 : {
448 : OSL_FAIL("AddRDFa: invalid arg: null RDFa attributes");
449 0 : return;
450 : }
451 0 : m_RDFaEntries.push_back(RDFaEntry(i_xObject, i_pRDFaAttributes));
452 : }
453 :
454 : void
455 0 : RDFaImportHelper::ParseAndAddRDFa(
456 : uno::Reference<rdf::XMetadatable> const & i_xObject,
457 : ::rtl::OUString const & i_rAbout,
458 : ::rtl::OUString const & i_rProperty,
459 : ::rtl::OUString const & i_rContent,
460 : ::rtl::OUString const & i_rDatatype)
461 : {
462 : ::boost::shared_ptr<ParsedRDFaAttributes> pAttributes(
463 0 : ParseRDFa(i_rAbout, i_rProperty, i_rContent, i_rDatatype) );
464 0 : if (pAttributes.get())
465 : {
466 0 : AddRDFa(i_xObject, pAttributes);
467 0 : }
468 0 : }
469 :
470 0 : void RDFaImportHelper::InsertRDFa(
471 : uno::Reference< rdf::XRepositorySupplier> const & i_xModel)
472 : {
473 : OSL_ENSURE(i_xModel.is(), "InsertRDFa: invalid arg: model null");
474 0 : if (!i_xModel.is()) return;
475 : const uno::Reference< rdf::XDocumentRepository > xRepository(
476 0 : i_xModel->getRDFRepository(), uno::UNO_QUERY);
477 : OSL_ENSURE(xRepository.is(), "InsertRDFa: no DocumentRepository?");
478 0 : if (!xRepository.is()) return;
479 0 : RDFaInserter inserter(GetImport().GetComponentContext(), xRepository);
480 : ::std::for_each(m_RDFaEntries.begin(), m_RDFaEntries.end(),
481 0 : ::boost::bind(&RDFaInserter::InsertRDFaEntry, &inserter, _1));
482 : }
483 :
484 72 : } // namespace xmloff
485 :
486 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|