diff options
Diffstat (limited to 'svtools/source/misc/templatefoldercache.cxx')
-rw-r--r-- | svtools/source/misc/templatefoldercache.cxx | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/svtools/source/misc/templatefoldercache.cxx b/svtools/source/misc/templatefoldercache.cxx new file mode 100644 index 0000000000..d8e473350e --- /dev/null +++ b/svtools/source/misc/templatefoldercache.cxx @@ -0,0 +1,796 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <osl/file.hxx> +#include <svtools/templatefoldercache.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/ucb/XDynamicResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/theOfficeInstallationDirectories.hpp> +#include <ucbhelper/content.hxx> +#include <osl/diagnose.h> +#include <rtl/ref.hxx> +#include <salhelper/simplereferenceobject.hxx> +#include <tools/time.hxx> +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <unotools/pathoptions.hxx> + +#include <comphelper/processfactory.hxx> + +#include <mutex> +#include <utility> +#include <vector> +#include <algorithm> + + +namespace svt +{ + + + using namespace ::utl; + using namespace ::com::sun::star; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::ucb; + using namespace ::com::sun::star::uno; + + + //= helpers + + + static SvStream& WriteDateTime( SvStream& _rStorage, const util::DateTime& _rDate ) + { + sal_uInt16 hundredthSeconds = static_cast< sal_uInt16 >( _rDate.NanoSeconds / tools::Time::nanoPerCenti ); + _rStorage.WriteUInt16( hundredthSeconds ); + + _rStorage.WriteUInt16( _rDate.Seconds ); + _rStorage.WriteUInt16( _rDate.Minutes ); + _rStorage.WriteUInt16( _rDate.Hours ); + _rStorage.WriteUInt16( _rDate.Day ); + _rStorage.WriteUInt16( _rDate.Month ); + _rStorage.WriteInt16( _rDate.Year ); + + return _rStorage; + } + + + static SvStream& operator >> ( SvStream& _rStorage, util::DateTime& _rDate ) + { + sal_uInt16 hundredthSeconds; + _rStorage.ReadUInt16( hundredthSeconds ); + _rDate.NanoSeconds = static_cast< sal_uInt32 >( hundredthSeconds ) * tools::Time::nanoPerCenti; + + _rStorage.ReadUInt16( _rDate.Seconds ); + _rStorage.ReadUInt16( _rDate.Minutes ); + _rStorage.ReadUInt16( _rDate.Hours ); + _rStorage.ReadUInt16( _rDate.Day ); + _rStorage.ReadUInt16( _rDate.Month ); + _rStorage.ReadInt16( _rDate.Year ); + + return _rStorage; + } + + //= TemplateContent + + namespace { + + struct TemplateContent; + + } + + typedef ::std::vector< ::rtl::Reference< TemplateContent > > TemplateFolderContent; + typedef TemplateFolderContent::const_iterator ConstFolderIterator; + typedef TemplateFolderContent::iterator FolderIterator; + + namespace { + + /** a struct describing one content in one of the template dirs (or at least it's relevant aspects) + */ + struct TemplateContent : public ::salhelper::SimpleReferenceObject + { + public: + + private: + INetURLObject m_aURL; + util::DateTime m_aLastModified; // date of last modification as reported by UCP + TemplateFolderContent m_aSubContents; // sorted (by name) list of the children + + private: + void implResetDate( ) + { + m_aLastModified.NanoSeconds = m_aLastModified.Seconds = m_aLastModified.Minutes = m_aLastModified.Hours = 0; + m_aLastModified.Day = m_aLastModified.Month = m_aLastModified.Year = 0; + } + + private: + virtual ~TemplateContent() override; + + public: + explicit TemplateContent( INetURLObject _aURL ); + + // attribute access + OUString getURL( ) const { return m_aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); } + void setModDate( const util::DateTime& _rDate ) { m_aLastModified = _rDate; } + const util::DateTime& getModDate( ) const { return m_aLastModified; } + + TemplateFolderContent& getSubContents() { return m_aSubContents; } + const TemplateFolderContent& getSubContents() const { return m_aSubContents; } + + ConstFolderIterator end() const { return m_aSubContents.end(); } + TemplateFolderContent::size_type + size() const { return m_aSubContents.size(); } + + void push_back( const ::rtl::Reference< TemplateContent >& _rxNewElement ) + { m_aSubContents.push_back( _rxNewElement ); } + }; + + } + + TemplateContent::TemplateContent( INetURLObject _aURL ) + :m_aURL(std::move( _aURL )) + { + DBG_ASSERT( INetProtocol::NotValid != m_aURL.GetProtocol(), "TemplateContent::TemplateContent: invalid URL!" ); + implResetDate(); + } + + + TemplateContent::~TemplateContent() + { + } + + + //= stl helpers + + namespace { + + /// compares two TemplateContent by URL + struct TemplateContentURLLess + { + bool operator() ( const ::rtl::Reference< TemplateContent >& _rxLHS, const ::rtl::Reference< TemplateContent >& _rxRHS ) const + { + return _rxLHS->getURL() < _rxRHS->getURL(); + } + }; + + + /// sorts the sib contents of a TemplateFolderContent + struct SubContentSort + { + void operator() ( TemplateFolderContent& _rFolder ) const + { + // sort the directory by name + ::std::sort( + _rFolder.begin(), + _rFolder.end(), + TemplateContentURLLess() + ); + + // sort the sub directories by name + ::std::for_each( + _rFolder.begin(), + _rFolder.end(), + *this + ); + } + + void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const + { + if ( _rxContent.is() && _rxContent->size() ) + { + operator()( _rxContent->getSubContents() ); + } + } + }; + + /** does a deep compare of two template contents + */ + struct TemplateContentEqual + { + + bool operator() (const ::rtl::Reference< TemplateContent >& _rLHS, const ::rtl::Reference< TemplateContent >& _rRHS ) + { + if ( !_rLHS.is() || !_rRHS.is() ) + { + OSL_FAIL( "TemplateContentEqual::operator(): invalid contents!" ); + return true; + // this is not strictly true, in case only one is invalid - but this is a heavy error anyway + } + + if ( _rLHS->getURL() != _rRHS->getURL() ) + return false; + + if ( _rLHS->getModDate() != _rRHS->getModDate() ) + return false; + + if ( _rLHS->getSubContents().size() != _rRHS->getSubContents().size() ) + return false; + + if ( !_rLHS->getSubContents().empty() ) + { // there are children + // -> compare them + ::std::pair< FolderIterator, FolderIterator > aFirstDifferent = ::std::mismatch( + _rLHS->getSubContents().begin(), + _rLHS->getSubContents().end(), + _rRHS->getSubContents().begin(), + *this + ); + if ( aFirstDifferent.first != _rLHS->getSubContents().end() ) + return false;// the sub contents differ + } + + return true; + } + }; + + + /// base class for functors which act on a SvStream + struct StorageHelper + { + protected: + SvStream& m_rStorage; + explicit StorageHelper( SvStream& _rStorage ) : m_rStorage( _rStorage ) { } + }; + + + struct StoreContentURL : public StorageHelper + { + uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs; + + StoreContentURL( SvStream& _rStorage, + uno::Reference< + util::XOfficeInstallationDirectories > xOfficeInstDirs ) + : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { } + + void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const + { + // use the base class operator with the local name of the content + OUString sURL = _rxContent->getURL(); + // #116281# Keep office installation relocatable. Never store + // any direct references to office installation directory. + sURL = m_xOfficeInstDirs->makeRelocatableURL( sURL ); + m_rStorage.WriteUniOrByteString( sURL, m_rStorage.GetStreamCharSet() ); + } + }; + + + /// functor which stores the complete content of a TemplateContent + struct StoreFolderContent : public StorageHelper + { + uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs; + + public: + StoreFolderContent( SvStream& _rStorage, + uno::Reference< + util::XOfficeInstallationDirectories > xOfficeInstDirs ) + : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { } + + + void operator() ( const TemplateContent& _rContent ) const + { + // store the info about this content + WriteDateTime( m_rStorage, _rContent.getModDate() ); + + // store the info about the children + // the number + m_rStorage.WriteInt32( _rContent.size() ); + // their URLs ( the local name is not enough, since URL might be not a hierarchical one, "expand:" for example ) + ::std::for_each( + _rContent.getSubContents().begin(), + _rContent.getSubContents().end(), + StoreContentURL( m_rStorage, m_xOfficeInstDirs ) + ); + // their content + ::std::for_each( + _rContent.getSubContents().begin(), + _rContent.getSubContents().end(), + *this + ); + } + + + void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const + { + if ( _rxContent.is() ) + { + operator()( *_rxContent ); + } + } + }; + + + /// functor which reads a complete TemplateContent instance + struct ReadFolderContent : public StorageHelper + { + uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs; + + ReadFolderContent( SvStream& _rStorage, + uno::Reference< + util::XOfficeInstallationDirectories > xOfficeInstDirs ) + : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { } + + + void operator() ( TemplateContent& _rContent ) const + { + // store the info about this content + util::DateTime aModDate; + m_rStorage >> aModDate; + _rContent.setModDate( aModDate ); + + // store the info about the children + // the number + sal_Int32 nChildren = 0; + m_rStorage.ReadInt32( nChildren ); + TemplateFolderContent& rChildren = _rContent.getSubContents(); + rChildren.resize( 0 ); + rChildren.reserve( nChildren ); + // initialize them with their (local) names + while ( nChildren-- ) + { + OUString sURL = m_rStorage.ReadUniOrByteString(m_rStorage.GetStreamCharSet()); + sURL = m_xOfficeInstDirs->makeAbsoluteURL( sURL ); + rChildren.push_back( new TemplateContent( INetURLObject( sURL ) ) ); + } + + // their content + ::std::for_each( + _rContent.getSubContents().begin(), + _rContent.getSubContents().end(), + *this + ); + } + + + void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const + { + if ( _rxContent.is() ) + { + operator()( *_rxContent ); + } + } + }; + + } + + //= TemplateFolderCacheImpl + + class TemplateFolderCacheImpl + { + private: + TemplateFolderContent m_aPreviousState; // the current state of the template dirs (as found on the HD) + TemplateFolderContent m_aCurrentState; // the previous state of the template dirs (as found in the cache file) + + std::mutex m_aMutex; + // will be lazy inited; never access directly; use getOfficeInstDirs(). + uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs; + + std::unique_ptr<SvStream> m_pCacheStream; + bool m_bNeedsUpdate : 1; + bool m_bKnowState : 1; + bool m_bValidCurrentState : 1; + bool m_bAutoStoreState : 1; + + public: + explicit TemplateFolderCacheImpl( bool _bAutoStoreState ); + ~TemplateFolderCacheImpl( ); + + bool needsUpdate(); + void storeState(); + + private: + bool openCacheStream( bool _bForRead ); + void closeCacheStream( ); + + /// read the state of the dirs from the cache file + bool readPreviousState(); + /// read the current state of the dirs + bool readCurrentState(); + + static OUString implParseSmart( const OUString& _rPath ); + + bool implReadFolder( const ::rtl::Reference< TemplateContent >& _rxRoot ); + + static sal_Int32 getMagicNumber(); + static void normalize( TemplateFolderContent& _rState ); + + // @return <TRUE/> if the states equal + static bool equalStates( const TemplateFolderContent& _rLHS, const TemplateFolderContent& _rRHS ); + + // late initialize m_xOfficeInstDirs + const uno::Reference< util::XOfficeInstallationDirectories >& getOfficeInstDirs(); + }; + + + TemplateFolderCacheImpl::TemplateFolderCacheImpl( bool _bAutoStoreState ) + :m_bNeedsUpdate ( true ) + ,m_bKnowState ( false ) + ,m_bValidCurrentState ( false ) + ,m_bAutoStoreState ( _bAutoStoreState ) + { + } + + + TemplateFolderCacheImpl::~TemplateFolderCacheImpl( ) + { + // store the current state if possible and required + if ( m_bValidCurrentState && m_bAutoStoreState ) + storeState(); + + closeCacheStream( ); + } + + + sal_Int32 TemplateFolderCacheImpl::getMagicNumber() + { + return (sal_Int8('T') << 12) + | (sal_Int8('D') << 8) + | (sal_Int8('S') << 4) + | (sal_Int8('C')); + } + + + void TemplateFolderCacheImpl::normalize( TemplateFolderContent& _rState ) + { + SubContentSort()( _rState ); + } + + + bool TemplateFolderCacheImpl::equalStates( const TemplateFolderContent& _rLHS, const TemplateFolderContent& _rRHS ) + { + if ( _rLHS.size() != _rRHS.size() ) + return false; + + // as both arrays are sorted (by definition - this is a precondition of this method) + // we can simply go from the front to the back and compare the single elements + + ::std::pair< ConstFolderIterator, ConstFolderIterator > aFirstDifferent = ::std::mismatch( + _rLHS.begin(), + _rLHS.end(), + _rRHS.begin(), + TemplateContentEqual() + ); + + return aFirstDifferent.first == _rLHS.end(); + } + + + void TemplateFolderCacheImpl::storeState() + { + if ( !m_bValidCurrentState ) + readCurrentState( ); + + if ( !(m_bValidCurrentState && openCacheStream( false )) ) + return; + + m_pCacheStream->WriteInt32( getMagicNumber() ); + + // store the template root folders + // the size + m_pCacheStream->WriteInt32( m_aCurrentState.size() ); + // the complete URLs + ::std::for_each( + m_aCurrentState.begin(), + m_aCurrentState.end(), + StoreContentURL( *m_pCacheStream, getOfficeInstDirs() ) + ); + + // the contents + ::std::for_each( + m_aCurrentState.begin(), + m_aCurrentState.end(), + StoreFolderContent( *m_pCacheStream, getOfficeInstDirs() ) + ); + } + + + OUString TemplateFolderCacheImpl::implParseSmart( const OUString& _rPath ) + { + INetURLObject aParser; + aParser.SetSmartProtocol( INetProtocol::File ); + aParser.SetURL( _rPath ); + if ( INetProtocol::NotValid == aParser.GetProtocol() ) + { + OUString sURL; + osl::FileBase::getFileURLFromSystemPath( _rPath, sURL ); + aParser.SetURL( sURL ); + } + return aParser.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + } + + + void TemplateFolderCacheImpl::closeCacheStream( ) + { + m_pCacheStream.reset(); + } + + + bool TemplateFolderCacheImpl::implReadFolder( const ::rtl::Reference< TemplateContent >& _rxRoot ) + { + try + { + // create a content for the current folder root + Reference< XResultSet > xResultSet; + Sequence< OUString > aContentProperties{ "Title", "DateModified", "DateCreated", + "IsFolder" }; + + // get the set of sub contents in the folder + try + { + Reference< XDynamicResultSet > xDynResultSet; + + ::ucbhelper::Content aTemplateRoot( _rxRoot->getURL(), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + xDynResultSet = aTemplateRoot.createDynamicCursor( aContentProperties ); + if ( xDynResultSet.is() ) + xResultSet = xDynResultSet->getStaticResultSet(); + } + catch( CommandAbortedException& ) + { + TOOLS_WARN_EXCEPTION( "svtools.misc", "" ); + return false; + } + catch( css::uno::Exception& ) + { + } + + // collect the infos about the sub contents + if ( xResultSet.is() ) + { + Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW ); + Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW ); + + while ( xResultSet->next() ) + { + INetURLObject aSubContentURL( xContentAccess->queryContentIdentifierString() ); + + // a new content instance + ::rtl::Reference< TemplateContent > xChild = new TemplateContent( std::move(aSubContentURL) ); + + // the modified date + xChild->setModDate( xRow->getTimestamp( 2 ) ); // date modified + if ( xRow->wasNull() ) + xChild->setModDate( xRow->getTimestamp( 3 ) ); // fallback: date created + + // push back this content + _rxRoot->push_back( xChild ); + + // is it a folder? + if ( xRow->getBoolean( 4 ) && !xRow->wasNull() ) + { // yes -> step down + ConstFolderIterator aNextLevelRoot = _rxRoot->end(); + --aNextLevelRoot; + implReadFolder( *aNextLevelRoot ); + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "svtools", "TemplateFolderCacheImpl::implReadFolder" ); + return false; + } + return true; + } + + + bool TemplateFolderCacheImpl::readCurrentState() + { + // reset + m_bValidCurrentState = false; + TemplateFolderContent aTemplateFolderContent; + m_aCurrentState.swap( aTemplateFolderContent ); + + // the template directories from the config + const SvtPathOptions aPathOptions; + const OUString& aDirs = aPathOptions.GetTemplatePath(); + + // loop through all the root-level template folders + sal_Int32 nIndex = 0; + do + { + OUString sTemplatePath( aDirs.getToken(0, ';', nIndex) ); + sTemplatePath = aPathOptions.ExpandMacros( sTemplatePath ); + + // Make sure excess ".." path segments (from expanding bootstrap + // variables in paths) are normalized in the same way they are + // normalized for paths read from the .templdir.cache file (where + // paths have gone through makeRelocatable URL on writing out and + // then through makeAbsoluteURL when reading back in), as otherwise + // equalStates() in needsUpdate() could erroneously consider + // m_aCurrentState and m_aPreviousState as different: + sTemplatePath = getOfficeInstDirs()->makeAbsoluteURL( + getOfficeInstDirs()->makeRelocatableURL(sTemplatePath)); + + // create a new entry + m_aCurrentState.push_back( new TemplateContent( INetURLObject( sTemplatePath ) ) ); + TemplateFolderContent::iterator aCurrentRoot = m_aCurrentState.end(); + --aCurrentRoot; + + if ( !implReadFolder( *aCurrentRoot ) ) + return false; + } + while ( nIndex >= 0 ); + + // normalize the array (which basically means "sort it") + normalize( m_aCurrentState ); + + m_bValidCurrentState = true; + return m_bValidCurrentState; + } + + + bool TemplateFolderCacheImpl::readPreviousState() + { + DBG_ASSERT( m_pCacheStream, "TemplateFolderCacheImpl::readPreviousState: not to be called without stream!" ); + + // reset + TemplateFolderContent aTemplateFolderContent; + m_aPreviousState.swap( aTemplateFolderContent ); + + // check the magic number + sal_Int32 nMagic = 0; + m_pCacheStream->ReadInt32( nMagic ); + DBG_ASSERT( getMagicNumber() == nMagic, "TemplateFolderCacheImpl::readPreviousState: invalid cache file!" ); + if ( getMagicNumber() != nMagic ) + return false; + + // the root directories + // their number + sal_Int32 nRootDirectories = 0; + m_pCacheStream->ReadInt32( nRootDirectories ); + // init empty TemplateContents with the URLs + m_aPreviousState.reserve( nRootDirectories ); + while ( nRootDirectories-- ) + { + OUString sURL = m_pCacheStream->ReadUniOrByteString(m_pCacheStream->GetStreamCharSet()); + // #116281# Keep office installation relocatable. Never store + // any direct references to office installation directory. + sURL = getOfficeInstDirs()->makeAbsoluteURL( sURL ); + m_aPreviousState.push_back( + new TemplateContent( INetURLObject(sURL) ) ); + } + + // read the contents of the root folders + ::std::for_each( + m_aPreviousState.begin(), + m_aPreviousState.end(), + ReadFolderContent( *m_pCacheStream, getOfficeInstDirs() ) + ); + + DBG_ASSERT( !m_pCacheStream->GetErrorCode(), "TemplateFolderCacheImpl::readPreviousState: unknown error during reading the state cache!" ); + + // normalize the array (which basically means "sort it") + normalize( m_aPreviousState ); + + return true; + } + + + bool TemplateFolderCacheImpl::openCacheStream( bool _bForRead ) + { + // close any old stream instance + closeCacheStream( ); + + // get the storage directory + OUString sStorageURL = implParseSmart( SvtPathOptions().GetStoragePath() ); + INetURLObject aStorageURL( sStorageURL ); + if ( INetProtocol::NotValid == aStorageURL.GetProtocol() ) + { + OSL_FAIL( "TemplateFolderCacheImpl::openCacheStream: invalid storage path!" ); + return false; + } + + // append our name + aStorageURL.Append( u".templdir.cache" ); + + // open the stream + m_pCacheStream = UcbStreamHelper::CreateStream( aStorageURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ), + _bForRead ? StreamMode::READ | StreamMode::NOCREATE : StreamMode::WRITE | StreamMode::TRUNC ); + DBG_ASSERT( m_pCacheStream, "TemplateFolderCacheImpl::openCacheStream: could not open/create the cache stream!" ); + if ( m_pCacheStream && m_pCacheStream->GetErrorCode() ) + { + m_pCacheStream.reset(); + } + + if ( m_pCacheStream ) + m_pCacheStream->SetStreamCharSet( RTL_TEXTENCODING_UTF8 ); + + return nullptr != m_pCacheStream; + } + + + bool TemplateFolderCacheImpl::needsUpdate() + { + if ( m_bKnowState ) + return m_bNeedsUpdate; + + m_bNeedsUpdate = true; + m_bKnowState = true; + + if ( readCurrentState() ) + { + // open the stream which contains the cached state of the directories + if ( openCacheStream( true ) ) + { // opening the stream succeeded + if ( readPreviousState() ) + { + m_bNeedsUpdate = !equalStates( m_aPreviousState, m_aCurrentState ); + } + else + { + closeCacheStream(); + } + } + } + return m_bNeedsUpdate; + } + + + const uno::Reference< util::XOfficeInstallationDirectories >& + TemplateFolderCacheImpl::getOfficeInstDirs() + { + if ( !m_xOfficeInstDirs.is() ) + { + std::lock_guard aGuard( m_aMutex ); + if ( !m_xOfficeInstDirs.is() ) + { + uno::Reference< uno::XComponentContext > xCtx( + comphelper::getProcessComponentContext() ); + m_xOfficeInstDirs = util::theOfficeInstallationDirectories::get(xCtx); + } + } + return m_xOfficeInstDirs; + } + + + //= TemplateFolderCache + + + TemplateFolderCache::TemplateFolderCache( bool _bAutoStoreState ) + :m_pImpl( new TemplateFolderCacheImpl( _bAutoStoreState ) ) + { + } + + + TemplateFolderCache::~TemplateFolderCache( ) + { + } + + + bool TemplateFolderCache::needsUpdate() + { + return m_pImpl->needsUpdate(); + } + + + void TemplateFolderCache::storeState() + { + m_pImpl->storeState(); + } + + +} // namespace sfx2 + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |