LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/ucb/source/ucp/tdoc - tdoc_provider.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 75 163 46.0 %
Date: 2013-07-09 Functions: 17 27 63.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             : /**************************************************************************
      22             :                                 TODO
      23             :  **************************************************************************
      24             : 
      25             :  *************************************************************************/
      26             : 
      27             : #include "rtl/ustrbuf.hxx"
      28             : 
      29             : #include "com/sun/star/container/XNameAccess.hpp"
      30             : #include "com/sun/star/embed/XStorage.hpp"
      31             : 
      32             : #include "comphelper/processfactory.hxx"
      33             : #include "ucbhelper/contentidentifier.hxx"
      34             : 
      35             : #include "tdoc_provider.hxx"
      36             : #include "tdoc_content.hxx"
      37             : #include "tdoc_uri.hxx"
      38             : #include "tdoc_docmgr.hxx"
      39             : #include "tdoc_storage.hxx"
      40             : 
      41             : using namespace com::sun::star;
      42             : using namespace tdoc_ucp;
      43             : 
      44             : //=========================================================================
      45             : //=========================================================================
      46             : //
      47             : // ContentProvider Implementation.
      48             : //
      49             : //=========================================================================
      50             : //=========================================================================
      51             : 
      52          31 : ContentProvider::ContentProvider(
      53             :             const uno::Reference< uno::XComponentContext >& rxContext )
      54             : : ::ucbhelper::ContentProviderImplHelper( rxContext ),
      55          31 :   m_xDocsMgr( new OfficeDocumentsManager( rxContext, this ) ),
      56          62 :   m_xStgElemFac( new StorageElementFactory( rxContext, m_xDocsMgr ) )
      57             : {
      58          31 : }
      59             : 
      60             : //=========================================================================
      61             : // virtual
      62          90 : ContentProvider::~ContentProvider()
      63             : {
      64          30 :     if ( m_xDocsMgr.is() )
      65          30 :         m_xDocsMgr->destroy();
      66          60 : }
      67             : 
      68             : //=========================================================================
      69             : //
      70             : // XInterface methods.
      71             : //
      72             : //=========================================================================
      73             : 
      74       11393 : XINTERFACE_IMPL_4( ContentProvider,
      75             :                    lang::XTypeProvider,
      76             :                    lang::XServiceInfo,
      77             :                    ucb::XContentProvider,
      78             :                    frame::XTransientDocumentsDocumentContentFactory );
      79             : 
      80             : //=========================================================================
      81             : //
      82             : // XTypeProvider methods.
      83             : //
      84             : //=========================================================================
      85             : 
      86           0 : XTYPEPROVIDER_IMPL_4( ContentProvider,
      87             :                       lang::XTypeProvider,
      88             :                       lang::XServiceInfo,
      89             :                       ucb::XContentProvider,
      90             :                       frame::XTransientDocumentsDocumentContentFactory );
      91             : 
      92             : //=========================================================================
      93             : //
      94             : // XServiceInfo methods.
      95             : //
      96             : //=========================================================================
      97             : 
      98         134 : XSERVICEINFO_IMPL_1_CTX(
      99             :     ContentProvider,
     100             :     OUString( "com.sun.star.comp.ucb.TransientDocumentsContentProvider" ),
     101             :     OUString( TDOC_CONTENT_PROVIDER_SERVICE_NAME ) );
     102             : 
     103             : //=========================================================================
     104             : //
     105             : // Service factory implementation.
     106             : //
     107             : //=========================================================================
     108             : 
     109          31 : ONE_INSTANCE_SERVICE_FACTORY_IMPL( ContentProvider );
     110             : 
     111             : //=========================================================================
     112             : //
     113             : // XContentProvider methods.
     114             : //
     115             : //=========================================================================
     116             : 
     117             : // virtual
     118             : uno::Reference< ucb::XContent > SAL_CALL
     119         160 : ContentProvider::queryContent(
     120             :         const uno::Reference< ucb::XContentIdentifier >& Identifier )
     121             :     throw( ucb::IllegalIdentifierException, uno::RuntimeException )
     122             : {
     123         160 :     Uri aUri( Identifier->getContentIdentifier() );
     124         160 :     if ( !aUri.isValid() )
     125             :         throw ucb::IllegalIdentifierException(
     126             :             OUString( "Invalid URL!" ),
     127           0 :             Identifier );
     128             : 
     129             :     // Normalize URI.
     130             :     uno::Reference< ucb::XContentIdentifier > xCanonicId
     131         320 :         = new ::ucbhelper::ContentIdentifier( aUri.getUri() );
     132             : 
     133         320 :     osl::MutexGuard aGuard( m_aMutex );
     134             : 
     135             :     // Check, if a content with given id already exists...
     136             :     uno::Reference< ucb::XContent > xContent
     137         160 :         = queryExistingContent( xCanonicId ).get();
     138             : 
     139         160 :     if ( !xContent.is() )
     140             :     {
     141             :         // Create a new content.
     142         160 :         xContent = Content::create( m_xContext, this, xCanonicId );
     143         160 :         registerNewContent( xContent );
     144             :     }
     145             : 
     146         320 :     return xContent;
     147             : }
     148             : 
     149             : //=========================================================================
     150             : //
     151             : // XTransientDocumentsDocumentContentFactory methods.
     152             : //
     153             : //=========================================================================
     154             : 
     155             : // virtual
     156             : uno::Reference< ucb::XContent > SAL_CALL
     157         145 : ContentProvider::createDocumentContent(
     158             :         const uno::Reference< frame::XModel >& Model )
     159             :     throw ( lang::IllegalArgumentException, uno::RuntimeException )
     160             : {
     161             :     // model -> id -> content identifier -> queryContent
     162         145 :     if ( m_xDocsMgr.is() )
     163             :     {
     164         145 :         OUString aDocId = m_xDocsMgr->queryDocumentId( Model );
     165         145 :         if ( !aDocId.isEmpty() )
     166             :         {
     167         145 :             OUStringBuffer aBuffer;
     168         145 :             aBuffer.appendAscii( TDOC_URL_SCHEME ":/" );
     169         145 :             aBuffer.append( aDocId );
     170             : 
     171             :             uno::Reference< ucb::XContentIdentifier > xId
     172         290 :                 = new ::ucbhelper::ContentIdentifier( aBuffer.makeStringAndClear() );
     173             : 
     174         290 :             osl::MutexGuard aGuard( m_aMutex );
     175             : 
     176             :             // Check, if a content with given id already exists...
     177             :             uno::Reference< ucb::XContent > xContent
     178         290 :                 = queryExistingContent( xId ).get();
     179             : 
     180         145 :             if ( !xContent.is() )
     181             :             {
     182             :                 // Create a new content.
     183         145 :                 xContent = Content::create( m_xContext, this, xId );
     184             :             }
     185             : 
     186         145 :             if ( xContent.is() )
     187         290 :                 return xContent;
     188             : 
     189             :             // no content.
     190             :             throw lang::IllegalArgumentException(
     191             :                 OUString(
     192             :                     "Illegal Content Identifier!" ),
     193             :                 static_cast< cppu::OWeakObject * >( this ),
     194         145 :                 1 );
     195             :         }
     196             :         else
     197             :         {
     198             :             throw lang::IllegalArgumentException(
     199             :                 OUString(
     200             :                     "Unable to obtain document id from model!" ),
     201             :                 static_cast< cppu::OWeakObject * >( this ),
     202           0 :                 1 );
     203         145 :         }
     204             :      }
     205             :      else
     206             :      {
     207             :         throw lang::IllegalArgumentException(
     208             :             OUString(
     209             :                 "No Document Manager!" ),
     210             :             static_cast< cppu::OWeakObject * >( this ),
     211           0 :             1 );
     212             :      }
     213             : }
     214             : 
     215             : //=========================================================================
     216             : //
     217             : // interface OfficeDocumentsEventListener
     218             : //
     219             : //=========================================================================
     220             : 
     221             : // virtual
     222         313 : void ContentProvider::notifyDocumentClosed( const OUString & rDocId )
     223             : {
     224         313 :     osl::MutexGuard aGuard( getContentListMutex() );
     225             : 
     226         626 :     ::ucbhelper::ContentRefList aAllContents;
     227         313 :     queryExistingContents( aAllContents );
     228             : 
     229         313 :     ::ucbhelper::ContentRefList::const_iterator it  = aAllContents.begin();
     230         313 :     ::ucbhelper::ContentRefList::const_iterator end = aAllContents.end();
     231             : 
     232             :     // Notify all content objects related to the closed doc.
     233             : 
     234         313 :     bool bFoundDocumentContent = false;
     235         626 :     rtl::Reference< Content > xRoot;
     236             : 
     237         626 :     while ( it != end )
     238             :     {
     239           0 :         Uri aUri( (*it)->getIdentifier()->getContentIdentifier() );
     240             :         OSL_ENSURE( aUri.isValid(),
     241             :                     "ContentProvider::notifyDocumentClosed - Invalid URI!" );
     242             : 
     243           0 :         if ( !bFoundDocumentContent )
     244             :         {
     245           0 :             if ( aUri.isRoot() )
     246             :             {
     247           0 :                 xRoot = static_cast< Content * >( (*it).get() );
     248             :             }
     249           0 :             else if ( aUri.isDocument() )
     250             :             {
     251           0 :                 if ( aUri.getDocumentId() == rDocId )
     252             :                 {
     253           0 :                     bFoundDocumentContent = true;
     254             : 
     255             :                     // document content will notify removal of child itself;
     256             :                     // no need for the root to propagate this.
     257           0 :                     xRoot.clear();
     258             :                 }
     259             :             }
     260             :         }
     261             : 
     262           0 :         if ( aUri.getDocumentId() == rDocId )
     263             :         {
     264             :             // Inform content.
     265             :             rtl::Reference< Content > xContent
     266           0 :                 = static_cast< Content * >( (*it).get() );
     267             : 
     268           0 :             xContent->notifyDocumentClosed();
     269             :         }
     270             : 
     271           0 :         ++it;
     272           0 :     }
     273             : 
     274         313 :     if ( xRoot.is() )
     275             :     {
     276             :         // No document content found for rDocId but root content
     277             :         // instanciated. Root content must announce document removal
     278             :         // to content event listeners.
     279           0 :         xRoot->notifyChildRemoved( rDocId );
     280         313 :     }
     281         313 : }
     282             : 
     283             : //=========================================================================
     284             : // virtual
     285         317 : void ContentProvider::notifyDocumentOpened( const OUString & rDocId )
     286             : {
     287         317 :     osl::MutexGuard aGuard( getContentListMutex() );
     288             : 
     289         634 :     ::ucbhelper::ContentRefList aAllContents;
     290         317 :     queryExistingContents( aAllContents );
     291             : 
     292         317 :     ::ucbhelper::ContentRefList::const_iterator it  = aAllContents.begin();
     293         317 :     ::ucbhelper::ContentRefList::const_iterator end = aAllContents.end();
     294             : 
     295             :     // Find root content. If instanciated let it propagate document insertion.
     296             : 
     297         634 :     while ( it != end )
     298             :     {
     299           0 :         Uri aUri( (*it)->getIdentifier()->getContentIdentifier() );
     300             :         OSL_ENSURE( aUri.isValid(),
     301             :                     "ContentProvider::notifyDocumentOpened - Invalid URI!" );
     302             : 
     303           0 :         if ( aUri.isRoot() )
     304             :         {
     305             :             rtl::Reference< Content > xRoot
     306           0 :                 = static_cast< Content * >( (*it).get() );
     307           0 :             xRoot->notifyChildInserted( rDocId );
     308             : 
     309             :             // Done.
     310           0 :             break;
     311             :         }
     312             : 
     313           0 :         ++it;
     314         317 :     }
     315         317 : }
     316             : 
     317             : //=========================================================================
     318             : //
     319             : // Non-UNO
     320             : //
     321             : //=========================================================================
     322             : 
     323             : uno::Reference< embed::XStorage >
     324         305 : ContentProvider::queryStorage( const OUString & rUri,
     325             :                                StorageAccessMode eMode ) const
     326             : {
     327         305 :     if ( m_xStgElemFac.is() )
     328             :     {
     329             :         try
     330             :         {
     331         305 :             return m_xStgElemFac->createStorage( rUri, eMode );
     332             :         }
     333           0 :         catch ( embed::InvalidStorageException const & )
     334             :         {
     335             :             OSL_FAIL( "Caught InvalidStorageException!" );
     336             :         }
     337           0 :         catch ( lang::IllegalArgumentException const & )
     338             :         {
     339             :             OSL_FAIL( "Caught IllegalArgumentException!" );
     340             :         }
     341           0 :         catch ( io::IOException const & )
     342             :         {
     343             :             // Okay to happen, for instance when the storage does not exist.
     344             :             //OSL_ENSURE( false, "Caught IOException!" );
     345             :         }
     346           0 :         catch ( embed::StorageWrappedTargetException const & )
     347             :         {
     348             :             OSL_FAIL( "Caught embed::StorageWrappedTargetException!" );
     349             :         }
     350             :     }
     351           0 :     return uno::Reference< embed::XStorage >();
     352             : }
     353             : 
     354             : //=========================================================================
     355             : uno::Reference< embed::XStorage >
     356           0 : ContentProvider::queryStorageClone( const OUString & rUri ) const
     357             : {
     358           0 :     if ( m_xStgElemFac.is() )
     359             :     {
     360             :         try
     361             :         {
     362           0 :             Uri aUri( rUri );
     363             :             uno::Reference< embed::XStorage > xParentStorage
     364           0 :                 = m_xStgElemFac->createStorage( aUri.getParentUri(), READ );
     365             :             uno::Reference< embed::XStorage > xStorage
     366           0 :                 = m_xStgElemFac->createTemporaryStorage();
     367             : 
     368           0 :             xParentStorage->copyStorageElementLastCommitTo(
     369           0 :                                 aUri.getDecodedName(), xStorage );
     370           0 :             return xStorage;
     371             :         }
     372           0 :         catch ( embed::InvalidStorageException const & )
     373             :         {
     374             :             OSL_FAIL( "Caught InvalidStorageException!" );
     375             :         }
     376           0 :         catch ( lang::IllegalArgumentException const & )
     377             :         {
     378             :             OSL_FAIL( "Caught IllegalArgumentException!" );
     379             :         }
     380           0 :         catch ( io::IOException const & )
     381             :         {
     382             :             // Okay to happen, for instance when the storage does not exist.
     383             :             //OSL_ENSURE( false, "Caught IOException!" );
     384             :         }
     385           0 :         catch ( embed::StorageWrappedTargetException const & )
     386             :         {
     387             :             OSL_FAIL( "Caught embed::StorageWrappedTargetException!" );
     388             :         }
     389             :     }
     390             : 
     391           0 :     return uno::Reference< embed::XStorage >();
     392             : }
     393             : 
     394             : //=========================================================================
     395             : uno::Reference< io::XInputStream >
     396           0 : ContentProvider::queryInputStream( const OUString & rUri,
     397             :                                    const OUString & rPassword ) const
     398             :     throw ( packages::WrongPasswordException )
     399             : {
     400           0 :     if ( m_xStgElemFac.is() )
     401             :     {
     402             :         try
     403             :         {
     404           0 :             return m_xStgElemFac->createInputStream( rUri, rPassword );
     405             :         }
     406           0 :         catch ( embed::InvalidStorageException const & )
     407             :         {
     408             :             OSL_FAIL( "Caught InvalidStorageException!" );
     409             :         }
     410           0 :         catch ( lang::IllegalArgumentException const & )
     411             :         {
     412             :             OSL_FAIL( "Caught IllegalArgumentException!" );
     413             :         }
     414           0 :         catch ( io::IOException const & )
     415             :         {
     416             :             OSL_FAIL( "Caught IOException!" );
     417             :         }
     418           0 :         catch ( embed::StorageWrappedTargetException const & )
     419             :         {
     420             :             OSL_FAIL( "Caught embed::StorageWrappedTargetException!" );
     421             :         }
     422             : //        catch ( packages::WrongPasswordException const & )
     423             : //        {
     424             : //            // the key provided is wrong; rethrow; to be handled by caller.
     425             : //            throw;
     426             : //        }
     427             :     }
     428           0 :     return uno::Reference< io::XInputStream >();
     429             : }
     430             : 
     431             : //=========================================================================
     432             : uno::Reference< io::XOutputStream >
     433           0 : ContentProvider::queryOutputStream( const OUString & rUri,
     434             :                                     const OUString & rPassword,
     435             :                                     bool bTruncate ) const
     436             :     throw ( packages::WrongPasswordException )
     437             : {
     438           0 :     if ( m_xStgElemFac.is() )
     439             :     {
     440             :         try
     441             :         {
     442             :             return
     443           0 :                 m_xStgElemFac->createOutputStream( rUri, rPassword, bTruncate );
     444             :         }
     445           0 :         catch ( embed::InvalidStorageException const & )
     446             :         {
     447             :             OSL_FAIL( "Caught InvalidStorageException!" );
     448             :         }
     449           0 :         catch ( lang::IllegalArgumentException const & )
     450             :         {
     451             :             OSL_FAIL( "Caught IllegalArgumentException!" );
     452             :         }
     453           0 :         catch ( io::IOException const & )
     454             :         {
     455             :             // Okay to happen, for instance when the storage does not exist.
     456             :             //OSL_ENSURE( false, "Caught IOException!" );
     457             :         }
     458           0 :         catch ( embed::StorageWrappedTargetException const & )
     459             :         {
     460             :             OSL_FAIL( "Caught embed::StorageWrappedTargetException!" );
     461             :         }
     462             : //        catch ( packages::WrongPasswordException const & )
     463             : //        {
     464             : //            // the key provided is wrong; rethrow; to be handled by caller.
     465             : //            throw;
     466             : //        }
     467             :     }
     468           0 :     return uno::Reference< io::XOutputStream >();
     469             : }
     470             : 
     471             : //=========================================================================
     472             : uno::Reference< io::XStream >
     473           0 : ContentProvider::queryStream( const OUString & rUri,
     474             :                               const OUString & rPassword,
     475             :                               bool bTruncate ) const
     476             :     throw ( packages::WrongPasswordException )
     477             : {
     478           0 :     if ( m_xStgElemFac.is() )
     479             :     {
     480             :         try
     481             :         {
     482           0 :             return m_xStgElemFac->createStream( rUri, rPassword, bTruncate );
     483             :         }
     484           0 :         catch ( embed::InvalidStorageException const & )
     485             :         {
     486             :             OSL_FAIL( "Caught InvalidStorageException!" );
     487             :         }
     488           0 :         catch ( lang::IllegalArgumentException const & )
     489             :         {
     490             :             OSL_FAIL( "Caught IllegalArgumentException!" );
     491             :         }
     492           0 :         catch ( io::IOException const & )
     493             :         {
     494             :             // Okay to happen, for instance when the storage does not exist.
     495             :             //OSL_ENSURE( false, "Caught IOException!" );
     496             :         }
     497           0 :         catch ( embed::StorageWrappedTargetException const & )
     498             :         {
     499             :             OSL_FAIL( "Caught embed::StorageWrappedTargetException!" );
     500             :         }
     501             : //        catch ( packages::WrongPasswordException const & )
     502             : //        {
     503             : //            // the key provided is wrong; rethrow; to be handled by caller.
     504             : //            throw;
     505             : //        }
     506             :     }
     507           0 :     return uno::Reference< io::XStream >();
     508             : }
     509             : 
     510             : //=========================================================================
     511           0 : bool ContentProvider::queryNamesOfChildren(
     512             :     const OUString & rUri, uno::Sequence< OUString > & rNames ) const
     513             : {
     514           0 :     Uri aUri( rUri );
     515           0 :     if ( aUri.isRoot() )
     516             :     {
     517             :         // special handling for root, which has no storage, but children.
     518           0 :         if ( m_xDocsMgr.is() )
     519             :         {
     520           0 :             rNames = m_xDocsMgr->queryDocuments();
     521           0 :             return true;
     522             :         }
     523             :     }
     524             :     else
     525             :     {
     526           0 :         if ( m_xStgElemFac.is() )
     527             :         {
     528             :             try
     529             :             {
     530             :                 uno::Reference< embed::XStorage > xStorage
     531           0 :                     = m_xStgElemFac->createStorage( rUri, READ );
     532             : 
     533             :                 OSL_ENSURE( xStorage.is(), "Got no Storage!" );
     534             : 
     535           0 :                 if ( xStorage.is() )
     536             :                 {
     537             :                     uno::Reference< container::XNameAccess > xNA(
     538           0 :                         xStorage, uno::UNO_QUERY );
     539             : 
     540             :                     OSL_ENSURE( xNA.is(), "Got no css.container.XNameAccess!" );
     541           0 :                     if ( xNA.is() )
     542             :                     {
     543           0 :                         rNames = xNA->getElementNames();
     544           0 :                         return true;
     545           0 :                     }
     546           0 :                 }
     547             :             }
     548           0 :             catch ( embed::InvalidStorageException const & )
     549             :             {
     550             :                 OSL_FAIL( "Caught InvalidStorageException!" );
     551             :             }
     552           0 :             catch ( lang::IllegalArgumentException const & )
     553             :             {
     554             :                 OSL_FAIL( "Caught IllegalArgumentException!" );
     555             :             }
     556           0 :             catch ( io::IOException const & )
     557             :             {
     558             :                 // Okay to happen, for instance if the storage does not exist.
     559             :                 //OSL_ENSURE( false, "Caught IOException!" );
     560             :             }
     561           0 :             catch ( embed::StorageWrappedTargetException const & )
     562             :             {
     563             :                 OSL_FAIL( "Caught embed::StorageWrappedTargetException!" );
     564             :             }
     565             :         }
     566             :     }
     567           0 :     return false;
     568             : }
     569             : 
     570             : //=========================================================================
     571             : OUString
     572         305 : ContentProvider::queryStorageTitle( const OUString & rUri ) const
     573             : {
     574         305 :     OUString aTitle;
     575             : 
     576         610 :     Uri aUri( rUri );
     577         305 :     if ( aUri.isRoot() )
     578             :     {
     579             :         // always empty.
     580           0 :         aTitle = OUString();
     581             :     }
     582         305 :     else if ( aUri.isDocument() )
     583             :     {
     584             :         // for documents, title shall not be derived from URL. It shall
     585             :         // be somethimg more 'speaking' than just the document UID.
     586         305 :         if ( m_xDocsMgr.is() )
     587         305 :             aTitle = m_xDocsMgr->queryStorageTitle( aUri.getDocumentId() );
     588             :     }
     589             :     else
     590             :     {
     591             :         // derive title from URL
     592           0 :         aTitle = aUri.getDecodedName();
     593             :     }
     594             : 
     595             :     OSL_ENSURE( !aTitle.isEmpty() || aUri.isRoot(),
     596             :                 "ContentProvider::queryStorageTitle - empty title!" );
     597         610 :     return aTitle;
     598             : }
     599             : 
     600             : //=========================================================================
     601             : uno::Reference< frame::XModel >
     602         160 : ContentProvider::queryDocumentModel( const OUString & rUri ) const
     603             : {
     604         160 :     uno::Reference< frame::XModel > xModel;
     605             : 
     606         160 :     if ( m_xDocsMgr.is() )
     607             :     {
     608         160 :         Uri aUri( rUri );
     609         160 :         xModel = m_xDocsMgr->queryDocumentModel( aUri.getDocumentId() );
     610             :     }
     611             : 
     612             :     OSL_ENSURE( xModel.is(),
     613             :                 "ContentProvider::queryDocumentModel - no model!" );
     614         160 :     return xModel;
     615             : }
     616             : 
     617             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10