bump product version to 5.0.4.1
[LibreOffice.git] / xmloff / source / core / RDFaImportHelper.cxx
blob0cf3061586d0b2de0bf367b815f7773fa4ff17f8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "RDFaImportHelper.hxx"
22 #include <xmloff/xmlimp.hxx>
23 #include <xmloff/nmspmap.hxx>
25 #include <comphelper/sequence.hxx>
27 #include <com/sun/star/rdf/URI.hpp>
28 #include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
29 #include <com/sun/star/rdf/XDocumentRepository.hpp>
31 #include <rtl/ustring.hxx>
33 #include <boost/bind.hpp>
34 #include <boost/iterator_adaptors.hpp>
35 #ifndef BOOST_ITERATOR_ADAPTOR_DWA053000_HPP_ // from iterator_adaptors.hpp
36 // N.B.: the check for the header guard _of a specific version of boost_
37 // is here so this may work on different versions of boost,
38 // which sadly put the goods in different header files
39 #include <boost/iterator/transform_iterator.hpp>
40 #endif
42 #include <map>
43 #include <iterator>
44 #include <functional>
45 #include <algorithm>
47 using namespace ::com::sun::star;
49 namespace xmloff {
51 /** a bit of context for parsing RDFa attributes */
52 class RDFaReader
54 const SvXMLImport & m_rImport;
56 const SvXMLImport & GetImport() const { return m_rImport; }
58 //FIXME: this is an ugly hack to workaround buggy SvXMLImport::GetAbsolute
59 OUString GetAbsoluteReference(OUString const & i_rURI) const
61 if (i_rURI.isEmpty() || i_rURI[0] == '#')
63 return GetImport().GetBaseURL() + i_rURI;
65 else
67 return GetImport().GetAbsoluteReference(i_rURI);
71 public:
72 RDFaReader(SvXMLImport const & i_rImport)
73 : m_rImport(i_rImport)
74 { }
76 // returns URI or blank node!
77 OUString ReadCURIE(OUString const & i_rCURIE) const;
79 std::vector< OUString >
80 ReadCURIEs(OUString const & i_rCURIEs) const;
82 OUString
83 ReadURIOrSafeCURIE( OUString const & i_rURIOrSafeCURIE) const;
86 /** helper to insert RDFa statements into the RDF repository */
87 class RDFaInserter
89 const uno::Reference<uno::XComponentContext> m_xContext;
90 uno::Reference< rdf::XDocumentRepository > m_xRepository;
92 typedef ::std::map< OUString, uno::Reference< rdf::XBlankNode > >
93 BlankNodeMap_t;
95 BlankNodeMap_t m_BlankNodeMap;
97 public:
98 RDFaInserter(uno::Reference<uno::XComponentContext> const & i_xContext,
99 uno::Reference< rdf::XDocumentRepository > const & i_xRepository)
100 : m_xContext(i_xContext)
101 , m_xRepository(i_xRepository)
104 uno::Reference< rdf::XBlankNode >
105 LookupBlankNode(OUString const & i_rNodeId );
107 uno::Reference< rdf::XURI >
108 MakeURI( OUString const & i_rURI) const;
110 uno::Reference< rdf::XResource>
111 MakeResource( OUString const & i_rResource);
113 void InsertRDFaEntry(struct RDFaEntry const & i_rEntry);
116 /** store parsed RDFa attributes */
117 struct ParsedRDFaAttributes
119 OUString m_About;
120 ::std::vector< OUString > m_Properties;
121 OUString m_Content;
122 OUString m_Datatype;
124 ParsedRDFaAttributes(
125 OUString const & i_rAbout,
126 ::std::vector< OUString > const & i_rProperties,
127 OUString const & i_rContent,
128 OUString const & i_rDatatype)
129 : m_About(i_rAbout)
130 , m_Properties(i_rProperties)
131 , m_Content(i_rContent)
132 , m_Datatype(i_rDatatype)
136 /** store metadatable object and its RDFa attributes */
137 struct RDFaEntry
139 uno::Reference<rdf::XMetadatable> m_xObject;
140 std::shared_ptr<ParsedRDFaAttributes> m_xRDFaAttributes;
142 RDFaEntry(uno::Reference<rdf::XMetadatable> const & i_xObject,
143 std::shared_ptr<ParsedRDFaAttributes> const& i_pRDFaAttributes)
144 : m_xObject(i_xObject)
145 , m_xRDFaAttributes(i_pRDFaAttributes)
149 static inline bool isWS(const sal_Unicode i_Char)
151 return ('\t' == i_Char) || ('\n' == i_Char) || ('\r' == i_Char)
152 || (' ' == i_Char);
155 static OUString splitAtWS(OUString & io_rString)
157 const sal_Int32 len( io_rString.getLength() );
158 sal_Int32 idxstt(0);
159 while ((idxstt < len) && ( isWS(io_rString[idxstt])))
160 ++idxstt; // skip leading ws
161 sal_Int32 idxend(idxstt);
162 while ((idxend < len) && (!isWS(io_rString[idxend])))
163 ++idxend; // the CURIE
164 const OUString ret(io_rString.copy(idxstt, idxend - idxstt));
165 io_rString = io_rString.copy(idxend); // rest
166 return ret;
169 OUString
170 RDFaReader::ReadCURIE(OUString const & i_rCURIE) const
172 // the RDFa spec says that a prefix is required (it may be empty: ":foo")
173 const sal_Int32 idx( i_rCURIE.indexOf(':') );
174 if (idx >= 0)
176 OUString Prefix;
177 OUString LocalName;
178 OUString Namespace;
179 sal_uInt16 nKey( GetImport().GetNamespaceMap()._GetKeyByAttrName(
180 i_rCURIE, &Prefix, &LocalName, &Namespace) );
181 if ( Prefix == "_" )
183 // eeek, it's a bnode!
184 // "_" is not a valid URI scheme => we can identify bnodes
185 return i_rCURIE;
187 else
189 SAL_WARN_IF(XML_NAMESPACE_NONE == nKey, "xmloff.core", "no namespace?");
190 if ((XML_NAMESPACE_UNKNOWN != nKey) &&
191 (XML_NAMESPACE_XMLNS != nKey))
193 // N.B.: empty LocalName is valid!
194 const OUString URI(Namespace + LocalName);
195 return GetAbsoluteReference(URI);
197 else
199 SAL_INFO("xmloff.core", "ReadCURIE: invalid CURIE: invalid prefix" );
200 return OUString();
204 SAL_INFO("xmloff.core", "ReadCURIE: invalid CURIE: no prefix" );
205 return OUString();
208 ::std::vector< OUString >
209 RDFaReader::ReadCURIEs(OUString const & i_rCURIEs) const
211 std::vector< OUString > vec;
212 OUString CURIEs(i_rCURIEs);
213 do {
214 OUString curie( splitAtWS(CURIEs) );
215 if (!curie.isEmpty())
217 const OUString uri(ReadCURIE(curie));
218 if (!uri.isEmpty())
220 vec.push_back(uri);
224 while (!CURIEs.isEmpty());
225 if (vec.empty())
227 SAL_INFO("xmloff.core", "ReadCURIEs: invalid CURIEs" );
229 return vec;
232 OUString
233 RDFaReader::ReadURIOrSafeCURIE(OUString const & i_rURIOrSafeCURIE) const
235 const sal_Int32 len(i_rURIOrSafeCURIE.getLength());
236 if (len && (i_rURIOrSafeCURIE[0] == '['))
238 if ((len >= 2) && (i_rURIOrSafeCURIE[len - 1] == ']'))
240 return ReadCURIE(i_rURIOrSafeCURIE.copy(1, len - 2));
242 else
244 SAL_INFO("xmloff.core", "ReadURIOrSafeCURIE: invalid SafeCURIE" );
245 return OUString();
248 else
250 if (i_rURIOrSafeCURIE.startsWith("_:")) // blank node
252 SAL_INFO("xmloff.core", "ReadURIOrSafeCURIE: invalid URI: scheme is _" );
253 return OUString();
255 else
257 return GetAbsoluteReference(i_rURIOrSafeCURIE);
262 uno::Reference< rdf::XBlankNode >
263 RDFaInserter::LookupBlankNode(OUString const & i_rNodeId )
265 uno::Reference< rdf::XBlankNode > & rEntry( m_BlankNodeMap[ i_rNodeId ] );
266 if (!rEntry.is())
268 rEntry = m_xRepository->createBlankNode();
270 return rEntry;
273 uno::Reference< rdf::XURI >
274 RDFaInserter::MakeURI( OUString const & i_rURI) const
276 if (i_rURI.startsWith("_:")) // blank node
278 SAL_INFO("xmloff.core", "MakeURI: cannot create URI for blank node");
279 return 0;
281 else
285 return rdf::URI::create( m_xContext, i_rURI );
287 catch (uno::Exception &)
289 SAL_WARN("xmloff.core", "MakeURI: cannot create URI");
290 return 0;
295 uno::Reference< rdf::XResource>
296 RDFaInserter::MakeResource( OUString const & i_rResource)
298 if (i_rResource.startsWith("_:")) // blank node
300 // we cannot use the blank node label as-is: it must be distinct
301 // from labels in other graphs, so create fresh ones per XML stream
302 // N.B.: content.xml and styles.xml are distinct graphs
303 OUString name( i_rResource.copy(2) );
304 const uno::Reference< rdf::XBlankNode > xBNode( LookupBlankNode(name) );
305 SAL_WARN_IF(!xBNode.is(), "xmloff.core", "no blank node?");
306 return uno::Reference<rdf::XResource>( xBNode, uno::UNO_QUERY);
308 else
310 return uno::Reference<rdf::XResource>( MakeURI( i_rResource ),
311 uno::UNO_QUERY);
315 /** i wrote this because c++ implementations cannot agree on which variant
316 of boost::bind and std::mem_fun_ref applied to Reference::is compiles */
317 class ref_is_null :
318 public ::std::unary_function<bool, const uno::Reference<rdf::XURI> & >
320 public:
321 bool operator() (const uno::Reference<rdf::XURI> & i_rRef)
323 return !i_rRef.is();
327 void RDFaInserter::InsertRDFaEntry(
328 struct RDFaEntry const & i_rEntry)
330 SAL_WARN_IF(!i_rEntry.m_xObject.is(), "xmloff.core", "InsertRDFaEntry: invalid arg: null object");
331 if (!i_rEntry.m_xObject.is()) return;
333 const uno::Reference< rdf::XResource > xSubject(
334 MakeResource( i_rEntry.m_xRDFaAttributes->m_About ) );
335 if (!xSubject.is())
337 return; // invalid
340 ::std::vector< uno::Reference< rdf::XURI > > predicates;
342 predicates.reserve(i_rEntry.m_xRDFaAttributes->m_Properties.size());
344 ::std::remove_copy_if(
345 ::boost::make_transform_iterator(
346 i_rEntry.m_xRDFaAttributes->m_Properties.begin(),
347 ::boost::bind(&RDFaInserter::MakeURI, this, _1)),
348 // argh, this must be the same type :(
349 ::boost::make_transform_iterator(
350 i_rEntry.m_xRDFaAttributes->m_Properties.end(),
351 ::boost::bind(&RDFaInserter::MakeURI, this, _1)),
352 ::std::back_inserter(predicates),
353 ref_is_null() );
354 // compiles only on wntmsci12
355 // ::boost::bind( ::std::logical_not<sal_Bool>(), ::boost::bind<sal_Bool>(&uno::Reference<rdf::XURI>::is, _1)));
356 // compiles on unxsoli4, wntsci12, but not unxlngi6
357 // ::boost::bind( ::std::logical_not<sal_Bool>(), ::boost::bind<sal_Bool, com::sun::star::uno::Reference<rdf::XURI> >(&uno::Reference<rdf::XURI>::is, _1)));
358 // compiles on unxsoli4, unxlngi6, but not wntsci12
359 // ::std::not1( ::std::mem_fun_ref(&uno::Reference<rdf::XURI>::is)) );
361 if (!predicates.size())
363 return; // invalid
366 uno::Reference<rdf::XURI> xDatatype;
367 if (!i_rEntry.m_xRDFaAttributes->m_Datatype.isEmpty())
369 xDatatype = MakeURI( i_rEntry.m_xRDFaAttributes->m_Datatype );
374 // N.B.: this will call xMeta->ensureMetadataReference, which is why
375 // this must be done _after_ importing the whole XML file,
376 // to prevent collision between generated ids and ids in the file
377 m_xRepository->setStatementRDFa(xSubject, comphelper::containerToSequence(predicates),
378 i_rEntry.m_xObject,
379 i_rEntry.m_xRDFaAttributes->m_Content, xDatatype);
381 catch (uno::Exception &)
383 SAL_WARN("xmloff.core", "InsertRDFaEntry: setStatementRDFa failed?");
387 RDFaImportHelper::RDFaImportHelper(const SvXMLImport & i_rImport)
388 : m_rImport(i_rImport)
392 RDFaImportHelper::~RDFaImportHelper()
396 std::shared_ptr<ParsedRDFaAttributes>
397 RDFaImportHelper::ParseRDFa(
398 OUString const & i_rAbout,
399 OUString const & i_rProperty,
400 OUString const & i_rContent,
401 OUString const & i_rDatatype)
403 if (i_rProperty.isEmpty())
405 SAL_INFO("xmloff.core", "AddRDFa: invalid input: xhtml:property empty");
406 return std::shared_ptr<ParsedRDFaAttributes>();
408 // must parse CURIEs here: need namespace declaration context
409 RDFaReader reader(GetImport());
410 const OUString about( reader.ReadURIOrSafeCURIE(i_rAbout) );
411 if (about.isEmpty()) {
412 return std::shared_ptr<ParsedRDFaAttributes>();
414 const ::std::vector< OUString > properties(
415 reader.ReadCURIEs(i_rProperty) );
416 if (!properties.size()) {
417 return std::shared_ptr<ParsedRDFaAttributes>();
419 const OUString datatype( !i_rDatatype.isEmpty()
420 ? reader.ReadCURIE(i_rDatatype)
421 : OUString() );
422 return std::shared_ptr<ParsedRDFaAttributes>(
423 new ParsedRDFaAttributes(about, properties, i_rContent, datatype));
426 void
427 RDFaImportHelper::AddRDFa(
428 uno::Reference<rdf::XMetadatable> const & i_xObject,
429 std::shared_ptr<ParsedRDFaAttributes> & i_pRDFaAttributes)
431 if (!i_xObject.is())
433 SAL_WARN("xmloff.core", "AddRDFa: invalid arg: null textcontent");
434 return;
436 if (!i_pRDFaAttributes.get())
438 SAL_WARN("xmloff.core", "AddRDFa: invalid arg: null RDFa attributes");
439 return;
441 m_RDFaEntries.push_back(RDFaEntry(i_xObject, i_pRDFaAttributes));
444 void
445 RDFaImportHelper::ParseAndAddRDFa(
446 uno::Reference<rdf::XMetadatable> const & i_xObject,
447 OUString const & i_rAbout,
448 OUString const & i_rProperty,
449 OUString const & i_rContent,
450 OUString const & i_rDatatype)
452 std::shared_ptr<ParsedRDFaAttributes> pAttributes(
453 ParseRDFa(i_rAbout, i_rProperty, i_rContent, i_rDatatype) );
454 if (pAttributes.get())
456 AddRDFa(i_xObject, pAttributes);
460 void RDFaImportHelper::InsertRDFa(
461 uno::Reference< rdf::XRepositorySupplier> const & i_xModel)
463 SAL_WARN_IF(!i_xModel.is(), "xmloff.core", "InsertRDFa: invalid arg: model null");
464 if (!i_xModel.is()) return;
465 const uno::Reference< rdf::XDocumentRepository > xRepository(
466 i_xModel->getRDFRepository(), uno::UNO_QUERY);
467 SAL_WARN_IF(!xRepository.is(), "xmloff.core", "InsertRDFa: no DocumentRepository?");
468 if (!xRepository.is()) return;
469 RDFaInserter inserter(GetImport().GetComponentContext(), xRepository);
470 ::std::for_each(m_RDFaEntries.begin(), m_RDFaEntries.end(),
471 ::boost::bind(&RDFaInserter::InsertRDFaEntry, &inserter, _1));
474 } // namespace xmloff
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */