LCOV - code coverage report
Current view: top level - xmloff/source/core - RDFaImportHelper.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 140 148 94.6 %
Date: 2015-06-13 12:38:46 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             : #include "RDFaImportHelper.hxx"
      21             : 
      22             : #include <xmloff/xmlimp.hxx>
      23             : #include <xmloff/nmspmap.hxx>
      24             : 
      25             : #include <comphelper/sequence.hxx>
      26             : 
      27             : #include <com/sun/star/rdf/URI.hpp>
      28             : #include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
      29             : #include <com/sun/star/rdf/XDocumentRepository.hpp>
      30             : 
      31             : #include <rtl/ustring.hxx>
      32             : 
      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
      41             : 
      42             : #include <map>
      43             : #include <iterator>
      44             : #include <functional>
      45             : #include <algorithm>
      46             : 
      47             : using namespace ::com::sun::star;
      48             : 
      49             : namespace xmloff {
      50             : 
      51             : /** a bit of context for parsing RDFa attributes */
      52             : class RDFaReader
      53             : {
      54             :     const SvXMLImport & m_rImport;
      55             : 
      56         140 :     const SvXMLImport & GetImport() const { return m_rImport; }
      57             : 
      58             :     //FIXME: this is an ugly hack to workaround buggy SvXMLImport::GetAbsolute
      59          79 :     OUString GetAbsoluteReference(OUString const & i_rURI) const
      60             :     {
      61          79 :         if (i_rURI.isEmpty() || i_rURI[0] == '#')
      62             :         {
      63           1 :             return GetImport().GetBaseURL() + i_rURI;
      64             :         }
      65             :         else
      66             :         {
      67          78 :             return GetImport().GetAbsoluteReference(i_rURI);
      68             :         }
      69             :     }
      70             : 
      71             : public:
      72          38 :     explicit RDFaReader(SvXMLImport const & i_rImport)
      73          38 :         : m_rImport(i_rImport)
      74          38 :     { }
      75             : 
      76             :     // returns URI or blank node!
      77             :     OUString ReadCURIE(OUString const & i_rCURIE) const;
      78             : 
      79             :     std::vector< OUString >
      80             :     ReadCURIEs(OUString const & i_rCURIEs) const;
      81             : 
      82             :     OUString
      83             :     ReadURIOrSafeCURIE( OUString const & i_rURIOrSafeCURIE) const;
      84             : };
      85             : 
      86             : /** helper to insert RDFa statements into the RDF repository */
      87           6 : class RDFaInserter
      88             : {
      89             :     const uno::Reference<uno::XComponentContext> m_xContext;
      90             :     uno::Reference< rdf::XDocumentRepository > m_xRepository;
      91             : 
      92             :     typedef ::std::map< OUString, uno::Reference< rdf::XBlankNode > >
      93             :         BlankNodeMap_t;
      94             : 
      95             :     BlankNodeMap_t m_BlankNodeMap;
      96             : 
      97             : public:
      98           6 :     RDFaInserter(uno::Reference<uno::XComponentContext> const & i_xContext,
      99             :             uno::Reference< rdf::XDocumentRepository > const & i_xRepository)
     100             :         : m_xContext(i_xContext)
     101           6 :         , m_xRepository(i_xRepository)
     102           6 :     {}
     103             : 
     104             :     uno::Reference< rdf::XBlankNode >
     105             :     LookupBlankNode(OUString const & i_rNodeId );
     106             : 
     107             :     uno::Reference< rdf::XURI >
     108             :     MakeURI( OUString const & i_rURI) const;
     109             : 
     110             :     uno::Reference< rdf::XResource>
     111             :     MakeResource( OUString const & i_rResource);
     112             : 
     113             :     void InsertRDFaEntry(struct RDFaEntry const & i_rEntry);
     114             : };
     115             : 
     116             : /** store parsed RDFa attributes */
     117          35 : struct ParsedRDFaAttributes
     118             : {
     119             :     OUString m_About;
     120             :     ::std::vector< OUString > m_Properties;
     121             :     OUString m_Content;
     122             :     OUString m_Datatype;
     123             : 
     124          35 :     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          35 :         , m_Datatype(i_rDatatype)
     133          35 :     { }
     134             : };
     135             : 
     136             : /** store metadatable object and its RDFa attributes */
     137         169 : struct RDFaEntry
     138             : {
     139             :     uno::Reference<rdf::XMetadatable> m_xObject;
     140             :     std::shared_ptr<ParsedRDFaAttributes> m_xRDFaAttributes;
     141             : 
     142          35 :     RDFaEntry(uno::Reference<rdf::XMetadatable> const & i_xObject,
     143             :             std::shared_ptr<ParsedRDFaAttributes> const& i_pRDFaAttributes)
     144             :         : m_xObject(i_xObject)
     145          35 :         , m_xRDFaAttributes(i_pRDFaAttributes)
     146          35 :     { }
     147             : };
     148             : 
     149         344 : static inline bool isWS(const sal_Unicode i_Char)
     150             : {
     151         344 :     return ('\t' == i_Char) || ('\n' == i_Char) || ('\r' == i_Char)
     152         688 :         || (' ' == i_Char);
     153             : }
     154             : 
     155          42 : static OUString splitAtWS(OUString & io_rString)
     156             : {
     157          42 :     const sal_Int32 len( io_rString.getLength() );
     158          42 :     sal_Int32 idxstt(0);
     159          95 :     while ((idxstt < len) && ( isWS(io_rString[idxstt])))
     160          11 :         ++idxstt; // skip leading ws
     161          42 :     sal_Int32 idxend(idxstt);
     162         369 :     while ((idxend < len) && (!isWS(io_rString[idxend])))
     163         285 :         ++idxend; // the CURIE
     164          42 :     const OUString ret(io_rString.copy(idxstt, idxend - idxstt));
     165          42 :     io_rString = io_rString.copy(idxend); // rest
     166          42 :     return ret;
     167             : }
     168             : 
     169             : OUString
     170          62 : RDFaReader::ReadCURIE(OUString const & i_rCURIE) const
     171             : {
     172             :     // the RDFa spec says that a prefix is required (it may be empty: ":foo")
     173          62 :     const sal_Int32 idx( i_rCURIE.indexOf(':') );
     174          62 :     if (idx >= 0)
     175             :     {
     176          61 :         OUString Prefix;
     177         122 :         OUString LocalName;
     178         122 :         OUString Namespace;
     179          61 :         sal_uInt16 nKey( GetImport().GetNamespaceMap()._GetKeyByAttrName(
     180          61 :             i_rCURIE, &Prefix, &LocalName, &Namespace) );
     181          61 :         if ( Prefix == "_" )
     182             :         {
     183             :             // eeek, it's a bnode!
     184             :             // "_" is not a valid URI scheme => we can identify bnodes
     185           8 :             return i_rCURIE;
     186             :         }
     187             :         else
     188             :         {
     189             :             SAL_WARN_IF(XML_NAMESPACE_NONE == nKey, "xmloff.core", "no namespace?");
     190          53 :             if ((XML_NAMESPACE_UNKNOWN != nKey) &&
     191             :                 (XML_NAMESPACE_XMLNS   != nKey))
     192             :             {
     193             :                 // N.B.: empty LocalName is valid!
     194          51 :                 const OUString URI(Namespace + LocalName);
     195          51 :                 return GetAbsoluteReference(URI);
     196             :             }
     197             :             else
     198             :             {
     199             :                 SAL_INFO("xmloff.core", "ReadCURIE: invalid CURIE: invalid prefix" );
     200           2 :                 return OUString();
     201             :             }
     202          61 :         }
     203             :     }
     204             :     SAL_INFO("xmloff.core", "ReadCURIE: invalid CURIE: no prefix" );
     205           1 :     return OUString();
     206             : }
     207             : 
     208             : ::std::vector< OUString >
     209          35 : RDFaReader::ReadCURIEs(OUString const & i_rCURIEs) const
     210             : {
     211          35 :     std::vector< OUString > vec;
     212          70 :     OUString CURIEs(i_rCURIEs);
     213          42 :     do {
     214          42 :       OUString curie( splitAtWS(CURIEs) );
     215          42 :       if (!curie.isEmpty())
     216             :       {
     217          41 :           const OUString uri(ReadCURIE(curie));
     218          41 :           if (!uri.isEmpty())
     219             :           {
     220          41 :               vec.push_back(uri);
     221          41 :           }
     222          42 :       }
     223             :     }
     224          42 :     while (!CURIEs.isEmpty());
     225          35 :     if (vec.empty())
     226             :     {
     227             :         SAL_INFO("xmloff.core", "ReadCURIEs: invalid CURIEs" );
     228             :     }
     229          70 :     return vec;
     230             : }
     231             : 
     232             : OUString
     233          38 : RDFaReader::ReadURIOrSafeCURIE(OUString const & i_rURIOrSafeCURIE) const
     234             : {
     235          38 :     const sal_Int32 len(i_rURIOrSafeCURIE.getLength());
     236          38 :     if (len && (i_rURIOrSafeCURIE[0] == '['))
     237             :     {
     238           9 :         if ((len >= 2) && (i_rURIOrSafeCURIE[len - 1] == ']'))
     239             :         {
     240           9 :             return ReadCURIE(i_rURIOrSafeCURIE.copy(1, len - 2));
     241             :         }
     242             :         else
     243             :         {
     244             :             SAL_INFO("xmloff.core", "ReadURIOrSafeCURIE: invalid SafeCURIE" );
     245           0 :             return OUString();
     246             :         }
     247             :     }
     248             :     else
     249             :     {
     250          29 :         if (i_rURIOrSafeCURIE.startsWith("_:")) // blank node
     251             :         {
     252             :             SAL_INFO("xmloff.core", "ReadURIOrSafeCURIE: invalid URI: scheme is _" );
     253           1 :             return OUString();
     254             :         }
     255             :         else
     256             :         {
     257          28 :             return GetAbsoluteReference(i_rURIOrSafeCURIE);
     258             :         }
     259             :     }
     260             : }
     261             : 
     262             : uno::Reference< rdf::XBlankNode >
     263           6 : RDFaInserter::LookupBlankNode(OUString const & i_rNodeId )
     264             : {
     265           6 :     uno::Reference< rdf::XBlankNode > & rEntry( m_BlankNodeMap[ i_rNodeId ] );
     266           6 :     if (!rEntry.is())
     267             :     {
     268           4 :         rEntry = m_xRepository->createBlankNode();
     269             :     }
     270           6 :     return rEntry;
     271             : }
     272             : 
     273             : uno::Reference< rdf::XURI >
     274         121 : RDFaInserter::MakeURI( OUString const & i_rURI) const
     275             : {
     276         121 :     if (i_rURI.startsWith("_:")) // blank node
     277             :     {
     278             :         SAL_INFO("xmloff.core", "MakeURI: cannot create URI for blank node");
     279           2 :         return 0;
     280             :     }
     281             :     else
     282             :     {
     283             :         try
     284             :         {
     285         119 :             return rdf::URI::create( m_xContext, i_rURI );
     286             :         }
     287           0 :         catch (uno::Exception &)
     288             :         {
     289             :             SAL_WARN("xmloff.core", "MakeURI: cannot create URI");
     290           0 :             return 0;
     291             :         }
     292             :     }
     293             : }
     294             : 
     295             : uno::Reference< rdf::XResource>
     296          35 : RDFaInserter::MakeResource( OUString const & i_rResource)
     297             : {
     298          35 :     if (i_rResource.startsWith("_:")) // blank node
     299             :     {
     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           6 :         OUString name( i_rResource.copy(2) );
     304          12 :         const uno::Reference< rdf::XBlankNode > xBNode( LookupBlankNode(name) );
     305             :         SAL_WARN_IF(!xBNode.is(), "xmloff.core", "no blank node?");
     306          12 :         return uno::Reference<rdf::XResource>( xBNode, uno::UNO_QUERY);
     307             :     }
     308             :     else
     309             :     {
     310             :         return uno::Reference<rdf::XResource>( MakeURI( i_rResource ),
     311          29 :             uno::UNO_QUERY);
     312             :     }
     313             : }
     314             : 
     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> & >
     319             : {
     320             : public:
     321          41 :     bool operator() (const uno::Reference<rdf::XURI> & i_rRef)
     322             :     {
     323          41 :         return !i_rRef.is();
     324             :     }
     325             : };
     326             : 
     327          35 : void RDFaInserter::InsertRDFaEntry(
     328             :     struct RDFaEntry const & i_rEntry)
     329             : {
     330             :     SAL_WARN_IF(!i_rEntry.m_xObject.is(), "xmloff.core", "InsertRDFaEntry: invalid arg: null object");
     331          36 :     if (!i_rEntry.m_xObject.is()) return;
     332             : 
     333             :     const uno::Reference< rdf::XResource > xSubject(
     334          35 :         MakeResource( i_rEntry.m_xRDFaAttributes->m_About ) );
     335          35 :     if (!xSubject.is())
     336             :     {
     337           0 :         return; // invalid
     338             :     }
     339             : 
     340          69 :     ::std::vector< uno::Reference< rdf::XURI > > predicates;
     341             : 
     342          35 :     predicates.reserve(i_rEntry.m_xRDFaAttributes->m_Properties.size());
     343             : 
     344             :     ::std::remove_copy_if(
     345             :         ::boost::make_transform_iterator(
     346          35 :             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          35 :             i_rEntry.m_xRDFaAttributes->m_Properties.end(),
     351             :             ::boost::bind(&RDFaInserter::MakeURI, this, _1)),
     352             :         ::std::back_inserter(predicates),
     353         105 :         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)) );
     360             : 
     361          35 :     if (!predicates.size())
     362             :     {
     363           1 :         return; // invalid
     364             :     }
     365             : 
     366          68 :     uno::Reference<rdf::XURI> xDatatype;
     367          34 :     if (!i_rEntry.m_xRDFaAttributes->m_Datatype.isEmpty())
     368             :     {
     369          11 :         xDatatype = MakeURI( i_rEntry.m_xRDFaAttributes->m_Datatype );
     370             :     }
     371             : 
     372             :     try
     373             :     {
     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          34 :         m_xRepository->setStatementRDFa(xSubject, comphelper::containerToSequence(predicates),
     378             :             i_rEntry.m_xObject,
     379          34 :             i_rEntry.m_xRDFaAttributes->m_Content, xDatatype);
     380             :     }
     381           0 :     catch (uno::Exception &)
     382             :     {
     383             :         SAL_WARN("xmloff.core", "InsertRDFaEntry: setStatementRDFa failed?");
     384          34 :     }
     385             : }
     386             : 
     387           6 : RDFaImportHelper::RDFaImportHelper(const SvXMLImport & i_rImport)
     388           6 :     : m_rImport(i_rImport)
     389             : {
     390           6 : }
     391             : 
     392           6 : RDFaImportHelper::~RDFaImportHelper()
     393             : {
     394           6 : }
     395             : 
     396             : std::shared_ptr<ParsedRDFaAttributes>
     397          40 : RDFaImportHelper::ParseRDFa(
     398             :     OUString const & i_rAbout,
     399             :     OUString const & i_rProperty,
     400             :     OUString const & i_rContent,
     401             :     OUString const & i_rDatatype)
     402             : {
     403          40 :     if (i_rProperty.isEmpty())
     404             :     {
     405             :         SAL_INFO("xmloff.core", "AddRDFa: invalid input: xhtml:property empty");
     406           2 :         return std::shared_ptr<ParsedRDFaAttributes>();
     407             :     }
     408             :     // must parse CURIEs here: need namespace declaration context
     409          38 :     RDFaReader reader(GetImport());
     410          38 :     const OUString about( reader.ReadURIOrSafeCURIE(i_rAbout) );
     411          38 :     if (about.isEmpty()) {
     412           3 :         return std::shared_ptr<ParsedRDFaAttributes>();
     413             :     }
     414             :     const ::std::vector< OUString > properties(
     415          70 :         reader.ReadCURIEs(i_rProperty) );
     416          35 :     if (!properties.size()) {
     417           0 :         return std::shared_ptr<ParsedRDFaAttributes>();
     418             :     }
     419          35 :     const OUString datatype( !i_rDatatype.isEmpty()
     420             :         ?   reader.ReadCURIE(i_rDatatype)
     421          70 :         :   OUString() );
     422             :     return std::shared_ptr<ParsedRDFaAttributes>(
     423          73 :             new ParsedRDFaAttributes(about, properties, i_rContent, datatype));
     424             : }
     425             : 
     426             : void
     427          35 : RDFaImportHelper::AddRDFa(
     428             :     uno::Reference<rdf::XMetadatable> const & i_xObject,
     429             :     std::shared_ptr<ParsedRDFaAttributes> & i_pRDFaAttributes)
     430             : {
     431          35 :     if (!i_xObject.is())
     432             :     {
     433             :         SAL_WARN("xmloff.core", "AddRDFa: invalid arg: null textcontent");
     434           0 :         return;
     435             :     }
     436          35 :     if (!i_pRDFaAttributes.get())
     437             :     {
     438             :         SAL_WARN("xmloff.core", "AddRDFa: invalid arg: null RDFa attributes");
     439           0 :         return;
     440             :     }
     441          35 :     m_RDFaEntries.push_back(RDFaEntry(i_xObject, i_pRDFaAttributes));
     442             : }
     443             : 
     444             : void
     445          36 : 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)
     451             : {
     452             :     std::shared_ptr<ParsedRDFaAttributes> pAttributes(
     453          36 :         ParseRDFa(i_rAbout, i_rProperty, i_rContent, i_rDatatype) );
     454          36 :     if (pAttributes.get())
     455             :     {
     456          31 :         AddRDFa(i_xObject, pAttributes);
     457          36 :     }
     458          36 : }
     459             : 
     460           6 : void RDFaImportHelper::InsertRDFa(
     461             :     uno::Reference< rdf::XRepositorySupplier> const & i_xModel)
     462             : {
     463             :     SAL_WARN_IF(!i_xModel.is(), "xmloff.core", "InsertRDFa: invalid arg: model null");
     464           6 :     if (!i_xModel.is()) return;
     465             :     const uno::Reference< rdf::XDocumentRepository > xRepository(
     466           6 :         i_xModel->getRDFRepository(), uno::UNO_QUERY);
     467             :     SAL_WARN_IF(!xRepository.is(), "xmloff.core", "InsertRDFa: no DocumentRepository?");
     468           6 :     if (!xRepository.is()) return;
     469          12 :     RDFaInserter inserter(GetImport().GetComponentContext(), xRepository);
     470             :     ::std::for_each(m_RDFaEntries.begin(), m_RDFaEntries.end(),
     471          12 :         ::boost::bind(&RDFaInserter::InsertRDFaEntry, &inserter, _1));
     472             : }
     473             : 
     474         456 : } // namespace xmloff
     475             : 
     476             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11