LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/xmloff/source/core - RDFaImportHelper.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 140 148 94.6 %
Date: 2013-07-09 Functions: 29 29 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10