1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: RDFaImportHelper.cxx,v $
10 * $Revision: 1.1.2.6 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "precompiled_xmloff.hxx"
33 #include "RDFaImportHelper.hxx"
35 #include <xmloff/xmlimp.hxx>
36 #include <xmloff/nmspmap.hxx>
38 #include <comphelper/sequenceasvector.hxx>
40 #include <tools/string.hxx> // for GetAbsoluteReference
42 #include <com/sun/star/rdf/URI.hpp>
43 #include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
44 #include <com/sun/star/rdf/XDocumentRepository.hpp>
46 #include <rtl/ustring.hxx>
48 #include <boost/bind.hpp>
49 #include <boost/iterator_adaptors.hpp>
50 #ifndef BOOST_ITERATOR_ADAPTOR_DWA053000_HPP_ // from iterator_adaptors.hpp
51 // N.B.: the check for the header guard _of a specific version of boost_
52 // is here so this may work on different versions of boost,
53 // which sadly put the goods in different header files
54 #include <boost/iterator/transform_iterator.hpp>
63 using namespace ::com::sun::star
;
67 /** a bit of context for parsing RDFa attributes */
68 class SAL_DLLPRIVATE RDFaReader
70 const SvXMLImport
& m_rImport
;
72 const SvXMLImport
& GetImport() const { return m_rImport
; }
74 //FIXME: this is an ugly hack to workaround buggy SvXMLImport::GetAbsolute
75 ::rtl::OUString
GetAbsoluteReference(::rtl::OUString
const & i_rURI
) const
77 if (!i_rURI
.getLength() || i_rURI
[0] == '#')
79 return GetImport().GetBaseURL() + i_rURI
;
83 return GetImport().GetAbsoluteReference(i_rURI
);
88 RDFaReader(SvXMLImport
const & i_rImport
)
89 : m_rImport(i_rImport
)
92 // returns URI or blank node!
93 ::rtl::OUString
ReadCURIE(::rtl::OUString
const & i_rCURIE
) const;
95 std::vector
< ::rtl::OUString
>
96 ReadCURIEs(::rtl::OUString
const & i_rCURIEs
) const;
99 ReadURIOrSafeCURIE( ::rtl::OUString
const & i_rURIOrSafeCURIE
) const;
102 /** helper to insert RDFa statements into the RDF repository */
103 class SAL_DLLPRIVATE RDFaInserter
105 const uno::Reference
<uno::XComponentContext
> m_xContext
;
106 uno::Reference
< rdf::XDocumentRepository
> m_xRepository
;
108 typedef ::std::map
< ::rtl::OUString
, uno::Reference
< rdf::XBlankNode
> >
111 BlankNodeMap_t m_BlankNodeMap
;
114 RDFaInserter(uno::Reference
<uno::XComponentContext
> const & i_xContext
,
115 uno::Reference
< rdf::XDocumentRepository
> const & i_xRepository
)
116 : m_xContext(i_xContext
)
117 , m_xRepository(i_xRepository
)
120 uno::Reference
< rdf::XBlankNode
>
121 LookupBlankNode(::rtl::OUString
const & i_rNodeId
);
123 uno::Reference
< rdf::XURI
>
124 MakeURI( ::rtl::OUString
const & i_rURI
) const;
126 uno::Reference
< rdf::XResource
>
127 MakeResource( ::rtl::OUString
const & i_rResource
);
129 void InsertRDFaEntry(struct RDFaEntry
const & i_rEntry
);
132 /** store metadatable object and its RDFa attributes */
133 struct SAL_DLLPRIVATE RDFaEntry
135 uno::Reference
<rdf::XMetadatable
> m_xObject
;
136 ::rtl::OUString m_About
;
137 ::std::vector
< ::rtl::OUString
> m_Properties
;
138 ::rtl::OUString m_Content
;
139 ::rtl::OUString m_Datatype
;
141 RDFaEntry(uno::Reference
<rdf::XMetadatable
> i_xObject
,
142 ::rtl::OUString
const & i_rAbout
,
143 ::std::vector
< ::rtl::OUString
> const & i_rProperties
,
144 ::rtl::OUString
const & i_rContent
,
145 ::rtl::OUString
const & i_rDatatype
)
146 : m_xObject(i_xObject
)
148 , m_Properties(i_rProperties
)
149 , m_Content(i_rContent
)
150 , m_Datatype(i_rDatatype
)
154 ////////////////////////////////////////////////////////////////////////////
157 static inline bool isWS(const sal_Unicode i_Char
)
159 return ('\t' == i_Char
) || ('\n' == i_Char
) || ('\r' == i_Char
)
163 static ::rtl::OUString
splitAtWS(::rtl::OUString
& io_rString
)
165 const sal_Int32
len( io_rString
.getLength() );
167 while ((idxstt
< len
) && ( isWS(io_rString
[idxstt
])))
168 ++idxstt
; // skip leading ws
169 sal_Int32
idxend(idxstt
);
170 while ((idxend
< len
) && (!isWS(io_rString
[idxend
])))
171 ++idxend
; // the CURIE
172 const ::rtl::OUString
ret(io_rString
.copy(idxstt
, idxend
- idxstt
));
173 io_rString
= io_rString
.copy(idxend
); // rest
178 RDFaReader::ReadCURIE(::rtl::OUString
const & i_rCURIE
) const
180 // the RDFa spec says that a prefix is required (it may be empty: ":foo")
181 const sal_Int32
idx( i_rCURIE
.indexOf(':') );
184 ::rtl::OUString Prefix
;
185 ::rtl::OUString LocalName
;
186 ::rtl::OUString Namespace
;
187 sal_uInt16
nKey( GetImport().GetNamespaceMap()._GetKeyByAttrName(
188 i_rCURIE
, &Prefix
, &LocalName
, &Namespace
) );
189 if (Prefix
.equalsAscii("_"))
191 // eeek, it's a bnode!
192 // "_" is not a valid URI scheme => we can identify bnodes
197 OSL_ENSURE(XML_NAMESPACE_NONE
!= nKey
, "no namespace?");
198 if ((XML_NAMESPACE_UNKNOWN
!= nKey
) &&
199 (XML_NAMESPACE_XMLNS
!= nKey
))
201 // N.B.: empty LocalName is valid!
202 const ::rtl::OUString
URI(Namespace
+ LocalName
);
203 // return GetImport().GetAbsoluteReference(URI);
204 return GetAbsoluteReference(URI
);
208 OSL_TRACE( "ReadCURIE: invalid CURIE: invalid prefix" );
209 return ::rtl::OUString();
215 OSL_TRACE( "ReadCURIE: invalid CURIE: no prefix" );
216 return ::rtl::OUString();
220 ::std::vector
< ::rtl::OUString
>
221 RDFaReader::ReadCURIEs(::rtl::OUString
const & i_rCURIEs
) const
223 std::vector
< ::rtl::OUString
> vec
;
224 ::rtl::OUString
CURIEs(i_rCURIEs
);
226 ::rtl::OUString
curie( splitAtWS(CURIEs
) );
227 if (curie
.getLength())
229 const ::rtl::OUString
uri(ReadCURIE(curie
));
236 while (CURIEs
.getLength());
239 OSL_TRACE( "ReadCURIEs: invalid CURIEs" );
245 RDFaReader::ReadURIOrSafeCURIE(::rtl::OUString
const & i_rURIOrSafeCURIE
) const
247 const sal_Int32
len(i_rURIOrSafeCURIE
.getLength());
248 if (len
&& (i_rURIOrSafeCURIE
[0] == '['))
250 if ((len
>= 2) && (i_rURIOrSafeCURIE
[len
- 1] == ']'))
252 return ReadCURIE(i_rURIOrSafeCURIE
.copy(1, len
- 2));
256 OSL_TRACE( "ReadURIOrSafeCURIE: invalid SafeCURIE" );
257 return ::rtl::OUString();
262 if (i_rURIOrSafeCURIE
.matchAsciiL("_:", 2)) // blank node
264 OSL_TRACE( "ReadURIOrSafeCURIE: invalid URI: scheme is _" );
265 return ::rtl::OUString();
269 // return GetImport().GetAbsoluteReference(i_rURIOrSafeCURIE);
270 return GetAbsoluteReference(i_rURIOrSafeCURIE
);
275 ////////////////////////////////////////////////////////////////////////////
277 uno::Reference
< rdf::XBlankNode
>
278 RDFaInserter::LookupBlankNode(::rtl::OUString
const & i_rNodeId
)
280 uno::Reference
< rdf::XBlankNode
> & rEntry( m_BlankNodeMap
[ i_rNodeId
] );
283 rEntry
= m_xRepository
->createBlankNode();
288 uno::Reference
< rdf::XURI
>
289 RDFaInserter::MakeURI( ::rtl::OUString
const & i_rURI
) const
291 if (i_rURI
.matchAsciiL("_:", 2)) // blank node
293 OSL_TRACE("MakeURI: cannot create URI for blank node");
300 return rdf::URI::create( m_xContext
, i_rURI
);
302 catch (uno::Exception
&)
304 OSL_ENSURE(false, "MakeURI: cannot create URI");
310 uno::Reference
< rdf::XResource
>
311 RDFaInserter::MakeResource( ::rtl::OUString
const & i_rResource
)
313 if (i_rResource
.matchAsciiL("_:", 2)) // blank node
315 // we cannot use the blank node label as-is: it must be distinct
316 // from labels in other graphs, so create fresh ones per XML stream
317 // N.B.: content.xml and styles.xml are distinct graphs
318 ::rtl::OUString
name( i_rResource
.copy(2) );
319 const uno::Reference
< rdf::XBlankNode
> xBNode( LookupBlankNode(name
) );
320 OSL_ENSURE(xBNode
.is(), "no blank node?");
321 return uno::Reference
<rdf::XResource
>( xBNode
, uno::UNO_QUERY
);
325 return uno::Reference
<rdf::XResource
>( MakeURI( i_rResource
),
330 /** i wrote this because c++ implementations cannot agree on which variant
331 of boost::bind and std::mem_fun_ref applied to Reference::is compiles */
333 public ::std::unary_function
<sal_Bool
, const uno::Reference
<rdf::XURI
> & >
336 sal_Bool
operator() (const uno::Reference
<rdf::XURI
> & i_rRef
)
342 void RDFaInserter::InsertRDFaEntry(
343 struct RDFaEntry
const & i_rEntry
)
345 OSL_ENSURE(i_rEntry
.m_xObject
.is(),
346 "InsertRDFaEntry: invalid arg: null object");
347 if (!i_rEntry
.m_xObject
.is()) return;
349 const uno::Reference
< rdf::XResource
> xSubject(
350 MakeResource( i_rEntry
.m_About
) );
356 ::comphelper::SequenceAsVector
< uno::Reference
< rdf::XURI
> > predicates
;
358 predicates
.reserve(i_rEntry
.m_Properties
.size());
360 ::std::remove_copy_if(
361 ::boost::make_transform_iterator(i_rEntry
.m_Properties
.begin(),
362 ::boost::bind(&RDFaInserter::MakeURI
, this, _1
)),
363 // argh, this must be the same type :(
364 ::boost::make_transform_iterator(i_rEntry
.m_Properties
.end(),
365 ::boost::bind(&RDFaInserter::MakeURI
, this, _1
)),
366 ::std::back_inserter(predicates
),
368 // compiles only on wntmsci12
369 // ::boost::bind( ::std::logical_not<sal_Bool>(), ::boost::bind<sal_Bool>(&uno::Reference<rdf::XURI>::is, _1)));
370 // compiles on unxsoli4, wntsci12, but not unxlngi6
371 // ::boost::bind( ::std::logical_not<sal_Bool>(), ::boost::bind<sal_Bool, com::sun::star::uno::Reference<rdf::XURI> >(&uno::Reference<rdf::XURI>::is, _1)));
372 // compiles on unxsoli4, unxlngi6, but not wntsci12
373 // ::std::not1( ::std::mem_fun_ref(&uno::Reference<rdf::XURI>::is)) );
375 if (!predicates
.size())
380 uno::Reference
<rdf::XURI
> xDatatype
;
381 if (i_rEntry
.m_Datatype
.getLength())
383 xDatatype
= MakeURI( i_rEntry
.m_Datatype
);
388 // N.B.: this will call xMeta->ensureMetadataReference, which is why
389 // this must be done _after_ importing the whole XML file,
390 // to prevent collision between generated ids and ids in the file
391 m_xRepository
->setStatementRDFa(xSubject
, predicates
.getAsConstList(),
392 i_rEntry
.m_xObject
, i_rEntry
.m_Content
, xDatatype
);
394 catch (uno::Exception
&)
396 OSL_ENSURE(false, "InsertRDFaEntry: setStatementRDFa failed?");
400 ////////////////////////////////////////////////////////////////////////////
402 RDFaImportHelper::RDFaImportHelper(const SvXMLImport
& i_rImport
)
403 : m_rImport(i_rImport
)
407 RDFaImportHelper::~RDFaImportHelper()
412 RDFaImportHelper::AddRDFa(
413 uno::Reference
<rdf::XMetadatable
> i_xObject
,
414 ::rtl::OUString
const & i_rAbout
,
415 ::rtl::OUString
const & i_rProperty
,
416 ::rtl::OUString
const & i_rContent
,
417 ::rtl::OUString
const & i_rDatatype
)
419 if (!i_rProperty
.getLength())
421 OSL_TRACE("AddRDFa: invalid input: xhtml:property empty");
426 OSL_ENSURE(false, "AddRDFa: invalid arg: null textcontent");
429 // must parse CURIEs here: need namespace declaration context
430 RDFaReader
reader(GetImport());
431 const ::rtl::OUString
about( reader
.ReadURIOrSafeCURIE(i_rAbout
) );
432 if (!about
.getLength()) return;
433 const ::std::vector
< ::rtl::OUString
> properties(
434 reader
.ReadCURIEs(i_rProperty
) );
435 if (!properties
.size()) return;
436 const ::rtl::OUString
datatype( i_rDatatype
.getLength()
437 ? reader
.ReadCURIE(i_rDatatype
)
438 : ::rtl::OUString() );
439 m_RDFaEntries
.push_back(RDFaEntry(i_xObject
,
440 about
, properties
, i_rContent
, datatype
));
443 void RDFaImportHelper::InsertRDFa(
444 uno::Reference
< rdf::XRepositorySupplier
> const & i_xModel
)
446 OSL_ENSURE(i_xModel
.is(), "InsertRDFa: invalid arg: model null");
447 if (!i_xModel
.is()) return;
448 const uno::Reference
< rdf::XDocumentRepository
> xRepository(
449 i_xModel
->getRDFRepository(), uno::UNO_QUERY
);
450 OSL_ENSURE(xRepository
.is(), "InsertRDFa: no DocumentRepository?");
451 if (!xRepository
.is()) return;
452 RDFaInserter
inserter(GetImport().GetComponentContext(), xRepository
);
453 ::std::for_each(m_RDFaEntries
.begin(), m_RDFaEntries
.end(),
454 ::boost::bind(&RDFaInserter::InsertRDFaEntry
, &inserter
, _1
));
457 } // namespace xmloff