Files
libreoffice/tools/source/rc/resmgr.cxx
Caolán McNamara d40971a478 MoreButtons cannot be loaded from .src anymore
Change-Id: Ib831895b33323dd478cc125b3bf93c43826ef59d
2014-03-13 11:18:36 +00:00

1648 lines
52 KiB
C++

/* -*- 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 <config_folders.h>
#include "sal/config.h"
#include <cassert>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <tools/debug.hxx>
#include <tools/stream.hxx>
#include <tools/resmgr.hxx>
#include <tools/rc.hxx>
#include <tools/rcid.h>
#include <osl/endian.h>
#include <osl/process.h>
#include <osl/thread.h>
#include <osl/file.hxx>
#include <osl/mutex.hxx>
#include <osl/signal.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/strbuf.hxx>
#include <sal/log.hxx>
#include <rtl/instance.hxx>
#include <rtl/bootstrap.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <i18nlangtag/mslangid.hxx>
#include <tools/simplerm.hxx>
#include <functional>
#include <algorithm>
#include <boost/unordered_map.hpp>
#include <list>
#include <set>
using namespace osl;
// for thread safety
static osl::Mutex* pResMgrMutex = NULL;
static osl::Mutex& getResMgrMutex()
{
if( !pResMgrMutex )
{
osl::Guard<osl::Mutex> aGuard( *osl::Mutex::getGlobalMutex() );
if( ! pResMgrMutex )
pResMgrMutex = new osl::Mutex();
}
return *pResMgrMutex;
}
struct ImpContent;
class InternalResMgr
{
friend class ResMgr;
friend class SimpleResMgr;
friend class ResMgrContainer;
ImpContent * pContent;
sal_uInt32 nOffCorrection;
sal_uInt8 * pStringBlock;
SvStream * pStm;
bool bEqual2Content;
sal_uInt32 nEntries;
OUString aFileName;
OUString aPrefix;
OUString aResName;
bool bSingular;
LanguageTag aLocale;
boost::unordered_map<sal_uInt64, int>* pResUseDump;
InternalResMgr( const OUString& rFileURL,
const OUString& aPrefix,
const OUString& aResName,
const LanguageTag& rLocale );
~InternalResMgr();
bool Create();
bool IsGlobalAvailable( RESOURCE_TYPE nRT, sal_uInt32 nId ) const;
void * LoadGlobalRes( RESOURCE_TYPE nRT, sal_uInt32 nId,
void **pResHandle );
public:
static void FreeGlobalRes( void *, void * );
};
class ResMgrContainer
{
static ResMgrContainer* pOneInstance;
struct ContainerElement
{
InternalResMgr* pResMgr;
OUString aFileURL;
int nRefCount;
int nLoadCount;
ContainerElement() :
pResMgr( NULL ),
nRefCount( 0 ),
nLoadCount( 0 )
{}
};
boost::unordered_map< OUString, ContainerElement, OUStringHash> m_aResFiles;
LanguageTag m_aDefLocale;
ResMgrContainer() : m_aDefLocale( LANGUAGE_SYSTEM) { init(); }
~ResMgrContainer();
void init();
public:
static ResMgrContainer& get();
static void release();
InternalResMgr* getResMgr( const OUString& rPrefix,
LanguageTag& rLocale,
bool bForceNewInstance = false
);
InternalResMgr* getNextFallback( InternalResMgr* pResMgr );
void freeResMgr( InternalResMgr* pResMgr );
void setDefLocale( const LanguageTag& rLocale )
{ m_aDefLocale = rLocale; }
const LanguageTag& getDefLocale() const
{ return m_aDefLocale; }
};
ResMgrContainer* ResMgrContainer::pOneInstance = NULL;
ResMgrContainer& ResMgrContainer::get()
{
if( ! pOneInstance )
pOneInstance = new ResMgrContainer();
return *pOneInstance;
}
ResMgrContainer::~ResMgrContainer()
{
for( boost::unordered_map< OUString, ContainerElement, OUStringHash >::iterator it =
m_aResFiles.begin(); it != m_aResFiles.end(); ++it )
{
OSL_TRACE( "Resource file %s loaded %d times",
OUStringToOString( it->second.aFileURL, osl_getThreadTextEncoding() ).getStr(),
it->second.nLoadCount );
delete it->second.pResMgr;
}
}
void ResMgrContainer::release()
{
delete pOneInstance;
pOneInstance = NULL;
}
void ResMgrContainer::init()
{
assert( m_aResFiles.empty() );
// get resource path
OUString uri("$BRAND_BASE_DIR/" LIBO_SHARE_RESOURCE_FOLDER "/");
rtl::Bootstrap::expandMacros(uri); //TODO: detect failure
// collect all possible resource files
Directory aDir( uri );
if( aDir.open() == FileBase::E_None )
{
DirectoryItem aItem;
while( aDir.getNextItem( aItem ) == FileBase::E_None )
{
FileStatus aStatus(osl_FileStatus_Mask_FileName);
if( aItem.getFileStatus( aStatus ) == FileBase::E_None )
{
OUString aFileName = aStatus.getFileName();
if( ! aFileName.endsWithIgnoreAsciiCase( ".res" ) )
continue;
OUString aResName = aFileName.copy( 0, aFileName.getLength() - strlen(".res") );
if( aResName.isEmpty() )
continue;
assert( m_aResFiles.find( aResName ) == m_aResFiles.end() );
m_aResFiles[ aResName ].aFileURL = uri + aFileName;
SAL_INFO(
"tools.rc",
"ResMgrContainer: " << aResName << " -> "
<< m_aResFiles[ aResName ].aFileURL );
}
}
}
else
SAL_WARN( "tools.rc", "opening dir " << uri << " failed" );
// set default language
LanguageType nLang = MsLangId::getSystemUILanguage();
m_aDefLocale.reset( nLang);
}
namespace
{
bool isAlreadyPureenUS(const LanguageTag &rLocale)
{
return ( rLocale.getLanguageType() == LANGUAGE_ENGLISH_US );
}
}
InternalResMgr* ResMgrContainer::getResMgr( const OUString& rPrefix,
LanguageTag& rLocale,
bool bForceNewInstance
)
{
LanguageTag aLocale( rLocale );
boost::unordered_map< OUString, ContainerElement, OUStringHash >::iterator it = m_aResFiles.end();
::std::vector< OUString > aFallbacks( aLocale.getFallbackStrings( true));
if (!isAlreadyPureenUS( aLocale))
aFallbacks.push_back( "en-US"); // last resort if all fallbacks fail
for (::std::vector< OUString >::const_iterator fb( aFallbacks.begin()); fb != aFallbacks.end(); ++fb)
{
OUString aSearch( rPrefix + *fb );
it = m_aResFiles.find( aSearch );
if( it != m_aResFiles.end() )
{
// ensure InternalResMgr existance
if( ! it->second.pResMgr )
{
InternalResMgr* pImp =
new InternalResMgr( it->second.aFileURL, rPrefix, it->first, aLocale );
if( ! pImp->Create() )
{
delete pImp;
continue;
}
it->second.pResMgr = pImp;
}
break;
}
}
// try if there is anything with this prefix at all
if( it == m_aResFiles.end() )
{
aLocale.reset( LANGUAGE_SYSTEM);
it = m_aResFiles.find( rPrefix );
if( it == m_aResFiles.end() )
{
for( it = m_aResFiles.begin(); it != m_aResFiles.end(); ++it )
{
if( it->first.matchIgnoreAsciiCase( rPrefix ) )
{
// ensure InternalResMgr existance
if( ! it->second.pResMgr )
{
InternalResMgr* pImp =
new InternalResMgr( it->second.aFileURL,
rPrefix,
it->first,
aLocale );
if( ! pImp->Create() )
{
delete pImp;
continue;
}
it->second.pResMgr = pImp;
}
// try to guess locale
sal_Int32 nIndex = rPrefix.getLength();
if (nIndex < it->first.getLength())
aLocale.reset( it->first.copy( nIndex));
else
{
SAL_WARN( "tools.rc", "ResMgrContainer::getResMgr: it->first " <<
it->first << " shorter than prefix " << rPrefix);
}
break;
}
}
}
}
// give up
if( it == m_aResFiles.end() )
{
OUString sURL = rPrefix + rLocale.getBcp47() + ".res";
if ( m_aResFiles.find(sURL) == m_aResFiles.end() )
{
m_aResFiles[ sURL ].aFileURL = sURL;
return getResMgr(rPrefix,rLocale,bForceNewInstance);
} // if ( m_aResFiles.find(sURL) == m_aResFiles.end() )
return NULL;
}
rLocale = aLocale;
// at this point it->second.pResMgr must be filled either by creating a new one
// (then the refcount is still 0) or because we already had one
InternalResMgr* pImp = it->second.pResMgr;
if( it->second.nRefCount == 0 )
it->second.nLoadCount++;
// for SimpleResMgr
if( bForceNewInstance )
{
if( it->second.nRefCount == 0 )
{
// shortcut: the match algorithm already created the InternalResMgr
// take it instead of creating yet another one
it->second.pResMgr = NULL;
pImp->bSingular = true;
}
else
{
pImp = new InternalResMgr( it->second.aFileURL, rPrefix, it->first, aLocale );
pImp->bSingular = true;
if( !pImp->Create() )
{
delete pImp;
pImp = NULL;
}
else
it->second.nLoadCount++;
}
}
else
it->second.nRefCount++;
return pImp;
}
InternalResMgr* ResMgrContainer::getNextFallback( InternalResMgr* pMgr )
{
/* TODO-BCP47: this is nasty, but the previous code simply stripped a
* locale's variant and country in subsequent calls to end up with language
* only and then fallback to en-US if all failed, so this is at least
* equivalent if not better. Maybe this method could be changed to get
* passed / remember a fallback list and an index within to pick the next.
* */
::std::vector< OUString > aFallbacks( pMgr->aLocale.getFallbackStrings( true));
// The first is the locale itself, use next fallback or en-US.
/* TODO: what happens if the chain is "en-US", "en" -> "en-US", ...
* This was already an issue with the previous code. */
LanguageTag aLocale( ((aFallbacks.size() > 1) ? aFallbacks[1] : OUString( "en-US")));
InternalResMgr* pNext = getResMgr( pMgr->aPrefix, aLocale, pMgr->bSingular );
// prevent recursion
if( pNext == pMgr || ( pNext && pNext->aResName.equals( pMgr->aResName ) ) )
{
if( pNext->bSingular )
delete pNext;
pNext = NULL;
}
return pNext;
}
void ResMgrContainer::freeResMgr( InternalResMgr* pResMgr )
{
if( pResMgr->bSingular )
delete pResMgr;
else
{
boost::unordered_map< OUString, ContainerElement, OUStringHash >::iterator it =
m_aResFiles.find( pResMgr->aResName );
if( it != m_aResFiles.end() )
{
DBG_ASSERT( it->second.nRefCount > 0, "InternalResMgr freed too often" );
if( it->second.nRefCount > 0 )
it->second.nRefCount--;
if( it->second.nRefCount == 0 )
{
delete it->second.pResMgr;
it->second.pResMgr = NULL;
}
}
}
}
#ifdef DBG_UTIL
void Resource::TestRes()
{
if( m_pResMgr )
m_pResMgr->TestStack( this );
}
#endif
struct ImpContent
{
sal_uInt64 nTypeAndId;
sal_uInt32 nOffset;
};
struct ImpContentLessCompare : public ::std::binary_function< ImpContent, ImpContent, bool>
{
inline bool operator() (const ImpContent& lhs, const ImpContent& rhs) const
{
return lhs.nTypeAndId < rhs.nTypeAndId;
}
};
static ResHookProc pImplResHookProc = 0;
InternalResMgr::InternalResMgr( const OUString& rFileURL,
const OUString& rPrefix,
const OUString& rResName,
const LanguageTag& rLocale )
: pContent( NULL )
, nOffCorrection( 0 )
, pStringBlock( NULL )
, pStm( NULL )
, bEqual2Content( true )
, nEntries( 0 )
, aFileName( rFileURL )
, aPrefix( rPrefix )
, aResName( rResName )
, bSingular( false )
, aLocale( rLocale )
, pResUseDump( 0 )
{
}
InternalResMgr::~InternalResMgr()
{
rtl_freeMemory(pContent);
rtl_freeMemory(pStringBlock);
delete pStm;
#ifdef DBG_UTIL
if( pResUseDump )
{
const sal_Char* pLogFile = getenv( "STAR_RESOURCE_LOGGING" );
if ( pLogFile )
{
SvFileStream aStm( OUString::createFromAscii( pLogFile ), STREAM_WRITE );
aStm.Seek( STREAM_SEEK_TO_END );
OStringBuffer aLine("FileName: ");
aLine.append(OUStringToOString(aFileName,
RTL_TEXTENCODING_UTF8));
aStm.WriteLine(aLine.makeStringAndClear());
for( boost::unordered_map<sal_uInt64, int>::const_iterator it = pResUseDump->begin();
it != pResUseDump->end(); ++it )
{
sal_uInt64 nKeyId = it->first;
aLine.append("Type/Id: ");
aLine.append(sal::static_int_cast< sal_Int32 >((nKeyId >> 32) & 0xFFFFFFFF));
aLine.append('/');
aLine.append(sal::static_int_cast< sal_Int32 >(nKeyId & 0xFFFFFFFF));
aStm.WriteLine(aLine.makeStringAndClear());
}
}
}
#endif
delete pResUseDump;
}
bool InternalResMgr::Create()
{
ResMgrContainer::get();
bool bDone = false;
pStm = new SvFileStream( aFileName, (STREAM_READ | STREAM_SHARE_DENYWRITE | STREAM_NOCREATE) );
if( pStm->GetError() == 0 )
{
sal_Int32 lContLen = 0;
pStm->Seek( STREAM_SEEK_TO_END );
/*
if( ( pInternalResMgr->pHead = (RSHEADER_TYPE *)mmap( 0, nResourceFileSize,
PROT_READ, MAP_PRIVATE,
fRes, 0 ) ) != (RSHEADER_TYPE *)-1)
*/
pStm->SeekRel( - (int)sizeof( lContLen ) );
pStm->Read( &lContLen, sizeof( lContLen ) );
// is bigendian, swab to the right endian
lContLen = ResMgr::GetLong( &lContLen );
pStm->SeekRel( -lContLen );
// allocate stored ImpContent data (12 bytes per unit)
sal_uInt8* pContentBuf = (sal_uInt8*)rtl_allocateMemory( lContLen );
pStm->Read( pContentBuf, lContLen );
// allocate ImpContent space (sizeof(ImpContent) per unit, not necessarily 12)
pContent = (ImpContent *)rtl_allocateMemory( sizeof(ImpContent)*lContLen/12 );
// Shorten to number of ImpContent
nEntries = (sal_uInt32)lContLen / 12;
bEqual2Content = true;
bool bSorted = true;
if( nEntries )
{
#ifdef DBG_UTIL
const sal_Char* pLogFile = getenv( "STAR_RESOURCE_LOGGING" );
if ( pLogFile )
{
pResUseDump = new boost::unordered_map<sal_uInt64, int>;
for( sal_uInt32 i = 0; i < nEntries; ++i )
(*pResUseDump)[pContent[i].nTypeAndId] = 1;
}
#endif
// swap the content to the right endian
pContent[0].nTypeAndId = ResMgr::GetUInt64( pContentBuf );
pContent[0].nOffset = ResMgr::GetLong( pContentBuf+8 );
sal_uInt32 nCount = nEntries - 1;
for( sal_uInt32 i = 0,j=1; i < nCount; ++i,++j )
{
// swap the content to the right endian
pContent[j].nTypeAndId = ResMgr::GetUInt64( pContentBuf + (12*j) );
pContent[j].nOffset = ResMgr::GetLong( pContentBuf + (12*j+8) );
if( pContent[i].nTypeAndId >= pContent[j].nTypeAndId )
bSorted = false;
if( (pContent[i].nTypeAndId & 0xFFFFFFFF00000000LL) == (pContent[j].nTypeAndId & 0xFFFFFFFF00000000LL)
&& pContent[i].nOffset >= pContent[j].nOffset )
bEqual2Content = false;
}
}
rtl_freeMemory( pContentBuf );
OSL_ENSURE( bSorted, "content not sorted" );
OSL_ENSURE( bEqual2Content, "resource structure wrong" );
if( !bSorted )
::std::sort(pContent,pContent+nEntries,ImpContentLessCompare());
// qsort( pContent, nEntries, sizeof( ImpContent ), Compare );
bDone = true;
}
return bDone;
}
bool InternalResMgr::IsGlobalAvailable( RESOURCE_TYPE nRT, sal_uInt32 nId ) const
{
// Anfang der Strings suchen
ImpContent aValue;
aValue.nTypeAndId = ((sal_uInt64(nRT) << 32) | nId);
ImpContent * pFind = ::std::lower_bound(pContent,
pContent + nEntries,
aValue,
ImpContentLessCompare());
return (pFind != (pContent + nEntries)) && (pFind->nTypeAndId == aValue.nTypeAndId);
}
void* InternalResMgr::LoadGlobalRes( RESOURCE_TYPE nRT, sal_uInt32 nId,
void **pResHandle )
{
#ifdef DBG_UTIL
if( pResUseDump )
pResUseDump->erase( (sal_uInt64(nRT) << 32) | nId );
#endif
// search beginning of string
ImpContent aValue;
aValue.nTypeAndId = ((sal_uInt64(nRT) << 32) | nId);
ImpContent* pEnd = (pContent + nEntries);
ImpContent* pFind = ::std::lower_bound( pContent,
pEnd,
aValue,
ImpContentLessCompare());
if( pFind && (pFind != pEnd) && (pFind->nTypeAndId == aValue.nTypeAndId) )
{
if( nRT == RSC_STRING && bEqual2Content )
{
// string optimization
if( !pStringBlock )
{
// search beginning of string
ImpContent * pFirst = pFind;
ImpContent * pLast = pFirst;
while( pFirst > pContent && ((pFirst -1)->nTypeAndId >> 32) == RSC_STRING )
--pFirst;
while( pLast < pEnd && (pLast->nTypeAndId >> 32) == RSC_STRING )
++pLast;
nOffCorrection = pFirst->nOffset;
sal_uInt32 nSize;
--pLast;
pStm->Seek( pLast->nOffset );
RSHEADER_TYPE aHdr;
pStm->Read( &aHdr, sizeof( aHdr ) );
nSize = pLast->nOffset + aHdr.GetGlobOff() - nOffCorrection;
pStringBlock = (sal_uInt8*)rtl_allocateMemory( nSize );
pStm->Seek( pFirst->nOffset );
pStm->Read( pStringBlock, nSize );
}
*pResHandle = pStringBlock;
return (sal_uInt8*)pStringBlock + pFind->nOffset - nOffCorrection;
} // if( nRT == RSC_STRING && bEqual2Content )
else
{
*pResHandle = 0;
RSHEADER_TYPE aHeader;
pStm->Seek( pFind->nOffset );
pStm->Read( &aHeader, sizeof( RSHEADER_TYPE ) );
void * pRes = rtl_allocateMemory( aHeader.GetGlobOff() );
memcpy( pRes, &aHeader, sizeof( RSHEADER_TYPE ) );
pStm->Read( (sal_uInt8*)pRes + sizeof( RSHEADER_TYPE ),
aHeader.GetGlobOff() - sizeof( RSHEADER_TYPE ) );
return pRes;
}
} // if( pFind && (pFind != pEnd) && (pFind->nTypeAndId == nValue) )
*pResHandle = 0;
return NULL;
}
void InternalResMgr::FreeGlobalRes( void * pResHandle, void * pResource )
{
if ( !pResHandle )
// Free allocated resource
rtl_freeMemory(pResource);
}
#ifdef DBG_UTIL
OUString GetTypeRes_Impl( const ResId& rTypeId )
{
// Return on resource errors
static bool bInUse = false;
OUString aTypStr(OUString::number(rTypeId.GetId()));
if ( !bInUse )
{
bInUse = true;
ResId aResId( sal_uInt32(RSCVERSION_ID), *rTypeId.GetResMgr() );
aResId.SetRT( RSC_VERSIONCONTROL );
if ( rTypeId.GetResMgr()->GetResource( aResId ) )
{
rTypeId.SetRT( RSC_STRING );
if ( rTypeId.GetResMgr()->IsAvailable( rTypeId ) )
{
aTypStr = rTypeId.toString();
// Set class pointer to the end
rTypeId.GetResMgr()->Increment( sizeof( RSHEADER_TYPE ) );
}
}
bInUse = false;
}
return aTypStr;
}
void ResMgr::RscError_Impl( const sal_Char* pMessage, ResMgr* pResMgr,
RESOURCE_TYPE nRT, sal_uInt32 nId,
std::vector< ImpRCStack >& rResStack, int nDepth )
{
// create a separate ResMgr with its own stack
// first get a second reference of the InternalResMgr
InternalResMgr* pImp =
ResMgrContainer::get().getResMgr( pResMgr->pImpRes->aPrefix,
pResMgr->pImpRes->aLocale,
true );
ResMgr* pNewResMgr = new ResMgr( pImp );
OStringBuffer aStr(OUStringToOString(pResMgr->GetFileName(),
RTL_TEXTENCODING_UTF8));
if (aStr.getLength())
aStr.append('\n');
aStr.append("Class: ");
aStr.append(OUStringToOString(GetTypeRes_Impl(ResId(nRT, *pNewResMgr)),
RTL_TEXTENCODING_UTF8));
aStr.append(", Id: ");
aStr.append(static_cast<sal_Int32>(nId));
aStr.append(". ");
aStr.append(pMessage);
aStr.append("\nResource Stack\n");
while( nDepth > 0 )
{
aStr.append("Class: ");
aStr.append(OUStringToOString(GetTypeRes_Impl(
ResId(rResStack[nDepth].pResource->GetRT(), *pNewResMgr)),
RTL_TEXTENCODING_UTF8));
aStr.append(", Id: ");
aStr.append(static_cast<sal_Int32>(
rResStack[nDepth].pResource->GetId()));
nDepth--;
}
// clean up
delete pNewResMgr;
OSL_FAIL(aStr.getStr());
}
#endif
static void RscException_Impl()
{
switch ( osl_raiseSignal( OSL_SIGNAL_USER_RESOURCEFAILURE, (void*)"" ) )
{
case osl_Signal_ActCallNextHdl:
abort();
case osl_Signal_ActIgnore:
return;
case osl_Signal_ActAbortApp:
abort();
default:
case osl_Signal_ActKillApp:
exit(-1);
}
}
void ImpRCStack::Init( ResMgr* pMgr, const Resource* pObj, sal_uInt32 Id )
{
pResource = NULL;
pClassRes = NULL;
Flags = RC_NOTYPE;
aResHandle = NULL;
pResObj = pObj;
nId = Id & ~RSC_DONTRELEASE; //TLX: Besser Init aendern
pResMgr = pMgr;
if ( !(Id & RSC_DONTRELEASE) )
Flags |= RC_AUTORELEASE;
}
void ImpRCStack::Clear()
{
pResource = NULL;
pClassRes = NULL;
Flags = RC_NOTYPE;
aResHandle = NULL;
pResObj = NULL;
nId = 0;
pResMgr = NULL;
}
static RSHEADER_TYPE* LocalResource( const ImpRCStack* pStack,
RESOURCE_TYPE nRTType,
sal_uInt32 nId )
{
// Returns position of the resource if found or NULL otherwise
RSHEADER_TYPE* pTmp; // Pointer to child resource
RSHEADER_TYPE* pEnd; // Pointer to the end of this resource
if ( pStack->pResource && pStack->pClassRes )
{
pTmp = (RSHEADER_TYPE*)
((sal_uInt8*)pStack->pResource + pStack->pResource->GetLocalOff());
pEnd = (RSHEADER_TYPE*)
((sal_uInt8*)pStack->pResource + pStack->pResource->GetGlobOff());
while ( pTmp != pEnd )
{
if ( pTmp->GetRT() == nRTType && pTmp->GetId() == nId )
return pTmp;
pTmp = (RSHEADER_TYPE*)((sal_uInt8*)pTmp + pTmp->GetGlobOff());
}
}
return NULL;
}
void* ResMgr::pEmptyBuffer = NULL;
void* ResMgr::getEmptyBuffer()
{
if( ! pEmptyBuffer )
pEmptyBuffer = rtl_allocateZeroMemory( 1024 );
return pEmptyBuffer;
}
void ResMgr::DestroyAllResMgr()
{
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pEmptyBuffer )
{
rtl_freeMemory( pEmptyBuffer );
pEmptyBuffer = NULL;
}
ResMgrContainer::release();
}
delete pResMgrMutex;
pResMgrMutex = NULL;
}
void ResMgr::Init( const OUString& rFileName )
{
(void) rFileName; // avoid warning about unused parameter
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if ( !pImpRes )
{
#ifdef DBG_UTIL
OStringBuffer aStr("Resourcefile not found:\n");
aStr.append(OUStringToOString(rFileName, RTL_TEXTENCODING_UTF8));
OSL_FAIL(aStr.getStr());
#endif
RscException_Impl();
}
#ifdef DBG_UTIL
else
{
void* aResHandle = 0; // Helper variable for resource handles
void* pVoid; // Pointer on the resource
pVoid = pImpRes->LoadGlobalRes( RSC_VERSIONCONTROL, RSCVERSION_ID,
&aResHandle );
if ( pVoid )
InternalResMgr::FreeGlobalRes( aResHandle, pVoid );
else
{
SAL_WARN("tools.rc", "Wrong version: " << pImpRes->aFileName);
}
}
#endif
nCurStack = -1;
aStack.clear();
pFallbackResMgr = pOriginalResMgr = NULL;
incStack();
}
ResMgr::ResMgr( InternalResMgr * pImpMgr )
{
pImpRes = pImpMgr;
Init( pImpMgr->aFileName );
}
ResMgr::~ResMgr()
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
ResMgrContainer::get().freeResMgr( pImpRes );
// clean up possible left rc stack frames
while( nCurStack > 0 )
{
if( ( aStack[nCurStack].Flags & (RC_GLOBAL | RC_NOTFOUND) ) == RC_GLOBAL )
InternalResMgr::FreeGlobalRes( aStack[nCurStack].aResHandle,
aStack[nCurStack].pResource );
nCurStack--;
}
}
void ResMgr::incStack()
{
nCurStack++;
if( nCurStack >= int(aStack.size()) )
aStack.push_back( ImpRCStack() );
aStack[nCurStack].Clear();
DBG_ASSERT( nCurStack < 32, "Resource stack unreasonably large" );
}
void ResMgr::decStack()
{
DBG_ASSERT( nCurStack > 0, "resource stack underrun !" );
if( (aStack[nCurStack].Flags & RC_FALLBACK_UP) )
{
nCurStack--;
// warning: this will delete *this, see below
pOriginalResMgr->decStack();
}
else
{
ImpRCStack& rTop = aStack[nCurStack];
if( (rTop.Flags & RC_FALLBACK_DOWN) )
{
#if OSL_DEBUG_LEVEL > 1
OSL_TRACE( "returning from fallback %s",
OUStringToOString(pFallbackResMgr->GetFileName(), osl_getThreadTextEncoding() ).getStr() );
#endif
delete pFallbackResMgr;
pFallbackResMgr = NULL;
}
nCurStack--;
}
}
#ifdef DBG_UTIL
void ResMgr::TestStack( const Resource* pResObj )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
int upperLimit = nCurStack;
if ( upperLimit < 0 )
{
OSL_FAIL( "resource stack underrun!" );
upperLimit = aStack.size() - 1;
}
else if ( upperLimit >= static_cast<int>(aStack.size()) )
{
OSL_FAIL( "stack occupation index > allocated stack size" );
upperLimit = aStack.size() - 1;
}
if ( DbgIsResource() )
{
for( int i = 1; i <= upperLimit; ++i )
{
if ( aStack[i].pResObj == pResObj )
{
RscError_Impl( "Resource not freed! ", this,
aStack[i].pResource->GetRT(),
aStack[i].pResource->GetId(),
aStack, i-1 );
}
}
}
}
#endif
bool ResMgr::IsAvailable( const ResId& rId, const Resource* pResObj ) const
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
bool bAvailable = false;
RSHEADER_TYPE* pClassRes = rId.GetpResource();
RESOURCE_TYPE nRT = rId.GetRT2();
sal_uInt32 nId = rId.GetId();
const ResMgr* pMgr = rId.GetResMgr();
if ( !pMgr )
pMgr = this;
if( pMgr->pFallbackResMgr )
{
ResId aId( rId );
aId.SetResMgr( NULL );
return pMgr->pFallbackResMgr->IsAvailable( aId, pResObj );
}
if ( !pResObj || pResObj == pMgr->aStack[pMgr->nCurStack].pResObj )
{
if ( !pClassRes )
pClassRes = LocalResource( &pMgr->aStack[pMgr->nCurStack], nRT, nId );
if ( pClassRes )
{
if ( pClassRes->GetRT() == nRT )
bAvailable = true;
}
}
if ( !pClassRes )
bAvailable = pMgr->pImpRes->IsGlobalAvailable( nRT, nId );
return bAvailable;
}
void* ResMgr::GetClass()
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
return pFallbackResMgr->GetClass();
return aStack[nCurStack].pClassRes;
}
bool ResMgr::GetResource( const ResId& rId, const Resource* pResObj )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
{
ResId aId( rId );
aId.SetResMgr( NULL );
return pFallbackResMgr->GetResource( aId, pResObj );
}
ResMgr* pMgr = rId.GetResMgr();
if ( pMgr && (this != pMgr) )
return pMgr->GetResource( rId, pResObj );
// normally Increment will pop the context; this is
// not possible in RC_NOTFOUND case, so pop a frame here
ImpRCStack* pTop = &aStack[nCurStack];
if( (pTop->Flags & RC_NOTFOUND) )
{
decStack();
}
RSHEADER_TYPE* pClassRes = rId.GetpResource();
RESOURCE_TYPE nRT = rId.GetRT2();
sal_uInt32 nId = rId.GetId();
incStack();
pTop = &aStack[nCurStack];
pTop->Init( pMgr, pResObj, nId |
(rId.IsAutoRelease() ? 0 : RSC_DONTRELEASE) );
if ( pClassRes )
{
if ( pClassRes->GetRT() == nRT )
pTop->pClassRes = pClassRes;
else
{
#ifdef DBG_UTIL
RscError_Impl( "Different class and resource type!",
this, nRT, nId, aStack, nCurStack-1 );
#endif
pTop->Flags |= RC_NOTFOUND;
pTop->pClassRes = getEmptyBuffer();
pTop->pResource = (RSHEADER_TYPE*)pTop->pClassRes;
return false;
}
}
else
{
OSL_ENSURE( nCurStack > 0, "stack of 1 to shallow" );
pTop->pClassRes = LocalResource( &aStack[nCurStack-1], nRT, nId );
}
if ( pTop->pClassRes )
// lokale Resource, nicht system Resource
pTop->pResource = (RSHEADER_TYPE *)pTop->pClassRes;
else
{
pTop->pClassRes = pImpRes->LoadGlobalRes( nRT, nId, &pTop->aResHandle );
if ( pTop->pClassRes )
{
pTop->Flags |= RC_GLOBAL;
pTop->pResource = (RSHEADER_TYPE *)pTop->pClassRes;
}
else
{
// try to get a fallback resource
pFallbackResMgr = CreateFallbackResMgr( rId, pResObj );
if( pFallbackResMgr )
{
pTop->Flags |= RC_FALLBACK_DOWN;
#ifdef DBG_UTIL
OStringBuffer aMess("found resource ");
aMess.append(static_cast<sal_Int32>(nId));
aMess.append(" in fallback ");
aMess.append(OUStringToOString(
pFallbackResMgr->GetFileName(),
osl_getThreadTextEncoding()));
aMess.append('\n');
RscError_Impl(aMess.getStr(),
this, nRT, nId, aStack, nCurStack-1);
#endif
}
else
{
#ifdef DBG_UTIL
RscError_Impl( "Cannot load resource! ",
this, nRT, nId, aStack, nCurStack-1 );
#endif
pTop->Flags |= RC_NOTFOUND;
pTop->pClassRes = getEmptyBuffer();
pTop->pResource = (RSHEADER_TYPE*)pTop->pClassRes;
return false;
}
}
}
return true;
}
void * ResMgr::GetResourceSkipHeader( const ResId& rResId, ResMgr ** ppResMgr )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
DBG_ASSERT( rResId.GetResMgr(), "illegal ResId without ResMgr" );
*ppResMgr = rResId.GetResMgr();
if( *ppResMgr )
{
(*ppResMgr)->GetResource( rResId );
(*ppResMgr)->Increment( sizeof( RSHEADER_TYPE ) );
return (*ppResMgr)->GetClass();
}
return getEmptyBuffer();
}
void ResMgr::PopContext( const Resource* pResObj )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
{
pFallbackResMgr->PopContext( pResObj );
return;
}
#ifdef DBG_UTIL
if ( DbgIsResource() )
{
if ( (aStack[nCurStack].pResObj != pResObj) || nCurStack == 0 )
{
RscError_Impl( "Cannot free resource! ", this,
RSC_NOTYPE, 0, aStack, nCurStack );
}
}
#endif
if ( nCurStack > 0 )
{
ImpRCStack* pTop = &aStack[nCurStack];
#ifdef DBG_UTIL
if ( DbgIsResource() && !(pTop->Flags & RC_NOTFOUND) )
{
void* pRes = (sal_uInt8*)pTop->pResource +
pTop->pResource->GetLocalOff();
if ( pTop->pClassRes != pRes )
{
RscError_Impl( "Classpointer not at the end!",
this, pTop->pResource->GetRT(),
pTop->pResource->GetId(),
aStack, nCurStack-1 );
}
}
#endif
// free resource
if( (pTop->Flags & (RC_GLOBAL | RC_NOTFOUND)) == RC_GLOBAL )
// free global resource if resource is foreign
InternalResMgr::FreeGlobalRes( pTop->aResHandle, pTop->pResource );
decStack();
}
}
RSHEADER_TYPE* ResMgr::CreateBlock( const ResId& rId )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
{
ResId aId( rId );
aId.SetResMgr( NULL );
return pFallbackResMgr->CreateBlock( aId );
}
RSHEADER_TYPE* pHeader = NULL;
if ( GetResource( rId ) )
{
// Pointer is at the beginning of the resource, thus
// class pointer points to the header, and the remaining size
// equals to total size of allocated memory
pHeader = (RSHEADER_TYPE*)rtl_allocateMemory( GetRemainSize() );
memcpy( pHeader, GetClass(), GetRemainSize() );
Increment( pHeader->GetLocalOff() ); //ans Ende setzen
if ( pHeader->GetLocalOff() != pHeader->GetGlobOff() )
// Has sub-resources, thus release them as well
PopContext();
}
return pHeader;
}
sal_Int16 ResMgr::GetShort( void * pShort )
{
return ((*((sal_uInt8*)pShort + 0) << 8) |
(*((sal_uInt8*)pShort + 1) << 0) );
}
sal_Int32 ResMgr::GetLong( void * pLong )
{
return ((*((sal_uInt8*)pLong + 0) << 24) |
(*((sal_uInt8*)pLong + 1) << 16) |
(*((sal_uInt8*)pLong + 2) << 8) |
(*((sal_uInt8*)pLong + 3) << 0) );
}
sal_uInt64 ResMgr::GetUInt64( void* pDatum )
{
return ((sal_uInt64(*((sal_uInt8*)pDatum + 0)) << 56) |
(sal_uInt64(*((sal_uInt8*)pDatum + 1)) << 48) |
(sal_uInt64(*((sal_uInt8*)pDatum + 2)) << 40) |
(sal_uInt64(*((sal_uInt8*)pDatum + 3)) << 32) |
(sal_uInt64(*((sal_uInt8*)pDatum + 4)) << 24) |
(sal_uInt64(*((sal_uInt8*)pDatum + 5)) << 16) |
(sal_uInt64(*((sal_uInt8*)pDatum + 6)) << 8) |
(sal_uInt64(*((sal_uInt8*)pDatum + 7)) << 0) );
}
sal_uInt32 ResMgr::GetStringWithoutHook( OUString& rStr, const sal_uInt8* pStr )
{
sal_uInt32 nLen=0;
sal_uInt32 nRet = GetStringSize( pStr, nLen );
const sal_Char* str = reinterpret_cast< const sal_Char* >( pStr );
OUString aString( str, strlen( str ), RTL_TEXTENCODING_UTF8,
RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_MAPTOPRIVATE |
RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT );
rStr = aString;
return nRet;
}
sal_uInt32 ResMgr::GetString( OUString& rStr, const sal_uInt8* pStr )
{
OUString aString;
sal_uInt32 nRet = GetStringWithoutHook( aString, pStr );
if ( pImplResHookProc )
aString = pImplResHookProc( aString );
rStr = aString;
return nRet;
}
sal_uInt32 ResMgr::GetByteString( OString& rStr, const sal_uInt8* pStr )
{
sal_uInt32 nLen=0;
sal_uInt32 nRet = GetStringSize( pStr, nLen );
rStr = OString( (const sal_Char*)pStr, nLen );
return nRet;
}
sal_uInt32 ResMgr::GetStringSize( const sal_uInt8* pStr, sal_uInt32& nLen )
{
nLen = static_cast< sal_uInt32 >( strlen( (const char*)pStr ) );
return GetStringSize( nLen );
}
sal_uInt32 ResMgr::GetRemainSize()
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
return pFallbackResMgr->GetRemainSize();
const ImpRCStack& rTop = aStack[nCurStack];
return (sal_uInt32)((sal_IntPtr)(sal_uInt8 *)rTop.pResource +
rTop.pResource->GetLocalOff() -
(sal_IntPtr)(sal_uInt8 *)rTop.pClassRes);
}
void* ResMgr::Increment( sal_uInt32 nSize )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
return pFallbackResMgr->Increment( nSize );
ImpRCStack& rStack = aStack[nCurStack];
if( (rStack.Flags & RC_NOTFOUND) )
return rStack.pClassRes;
sal_uInt8* pClassRes = (sal_uInt8*)rStack.pClassRes + nSize;
rStack.pClassRes = pClassRes;
RSHEADER_TYPE* pRes = rStack.pResource;
sal_uInt32 nLocalOff = pRes->GetLocalOff();
if ( (pRes->GetGlobOff() == nLocalOff) &&
(((char*)pRes + nLocalOff) == rStack.pClassRes) &&
(rStack.Flags & RC_AUTORELEASE))
{
PopContext( rStack.pResObj );
}
return pClassRes;
}
ResMgr* ResMgr::CreateFallbackResMgr( const ResId& rId, const Resource* pResource )
{
ResMgr *pFallback = NULL;
if( nCurStack > 0 )
{
// get the next fallback level in resource file scope
InternalResMgr* pRes = ResMgrContainer::get().getNextFallback( pImpRes );
if( pRes )
{
// check that the fallback locale is not already in the chain of
// fallbacks - prevent fallback loops
ResMgr* pResMgr = this;
while( pResMgr && (pResMgr->pImpRes->aLocale != pRes->aLocale))
{
pResMgr = pResMgr->pOriginalResMgr;
}
if( pResMgr )
{
// found a recursion, no fallback possible
ResMgrContainer::get().freeResMgr( pRes );
return NULL;
}
OSL_TRACE( "trying fallback: %s", OUStringToOString( pRes->aFileName, osl_getThreadTextEncoding() ).getStr() );
pFallback = new ResMgr( pRes );
pFallback->pOriginalResMgr = this;
// try to recreate the resource stack
bool bHaveStack = true;
for( int i = 1; i < nCurStack; i++ )
{
if( !aStack[i].pResource )
{
bHaveStack = false;
break;
}
ResId aId( aStack[i].pResource->GetId(), *pFallbackResMgr );
aId.SetRT( aStack[i].pResource->GetRT() );
if( !pFallback->GetResource( aId ) )
{
bHaveStack = false;
break;
}
}
if( bHaveStack )
{
ResId aId( rId.GetId(), *pFallback );
aId.SetRT( rId.GetRT() );
if( !pFallback->GetResource( aId, pResource ) )
bHaveStack = false;
else
pFallback->aStack[pFallback->nCurStack].Flags |= RC_FALLBACK_UP;
}
if( !bHaveStack )
{
delete pFallback;
pFallback = NULL;
}
}
}
return pFallback;
}
ResMgr* ResMgr::CreateResMgr( const sal_Char* pPrefixName,
LanguageTag aLocale )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
OUString aPrefix( pPrefixName, strlen( pPrefixName ), osl_getThreadTextEncoding() );
if( aLocale.isSystemLocale() )
aLocale = ResMgrContainer::get().getDefLocale();
InternalResMgr* pImp = ResMgrContainer::get().getResMgr( aPrefix, aLocale );
return pImp ? new ResMgr( pImp ) : NULL;
}
ResMgr* ResMgr::SearchCreateResMgr(
const sal_Char* pPrefixName,
LanguageTag& rLocale )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
OUString aPrefix( pPrefixName, strlen( pPrefixName ), osl_getThreadTextEncoding() );
if( rLocale.isSystemLocale() )
rLocale = ResMgrContainer::get().getDefLocale();
InternalResMgr* pImp = ResMgrContainer::get().getResMgr( aPrefix, rLocale );
return pImp ? new ResMgr( pImp ) : NULL;
}
sal_Int16 ResMgr::ReadShort()
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
return pFallbackResMgr->ReadShort();
sal_Int16 n = GetShort( GetClass() );
Increment( sizeof( sal_Int16 ) );
return n;
}
sal_Int32 ResMgr::ReadLong()
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
return pFallbackResMgr->ReadLong();
sal_Int32 n = GetLong( GetClass() );
Increment( sizeof( sal_Int32 ) );
return n;
}
OUString ResMgr::ReadStringWithoutHook()
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
return pFallbackResMgr->ReadStringWithoutHook();
OUString aRet;
const ImpRCStack& rTop = aStack[nCurStack];
if( (rTop.Flags & RC_NOTFOUND) )
{
#if OSL_DEBUG_LEVEL > 0
aRet = "<resource not found>";
#endif
}
else
Increment( GetStringWithoutHook( aRet, (const sal_uInt8*)GetClass() ) );
return aRet;
}
OUString ResMgr::ReadString()
{
OUString aRet = ReadStringWithoutHook();
if ( pImplResHookProc )
aRet = pImplResHookProc( aRet );
return aRet;
}
OString ResMgr::ReadByteString()
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
return pFallbackResMgr->ReadByteString();
OString aRet;
const ImpRCStack& rTop = aStack[nCurStack];
if( (rTop.Flags & RC_NOTFOUND) )
{
#if OSL_DEBUG_LEVEL > 0
aRet = OString( "<resource not found>" );
#endif
}
else
Increment( GetByteString( aRet, (const sal_uInt8*)GetClass() ) );
return aRet;
}
OString ResMgr::GetAutoHelpId()
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( pFallbackResMgr )
return pFallbackResMgr->GetAutoHelpId();
OSL_ENSURE( nCurStack, "resource stack empty in Auto help id generation" );
if( nCurStack < 1 || nCurStack > 2 )
return OString();
// prepare HID, start with resource prefix
OStringBuffer aHID( 32 );
aHID.append( OUStringToOString( pImpRes->aPrefix, RTL_TEXTENCODING_UTF8 ) );
aHID.append( '.' );
// append type
const ImpRCStack *pRC = StackTop();
OSL_ENSURE( pRC, "missing resource stack level" );
if ( nCurStack == 1 )
{
// auto help ids for top level windows
switch( pRC->pResource->GetRT() ) {
case RSC_DOCKINGWINDOW: aHID.append( "DockingWindow" ); break;
case RSC_WORKWIN: aHID.append( "WorkWindow" ); break;
case RSC_MODELESSDIALOG: aHID.append( "ModelessDialog" ); break;
case RSC_FLOATINGWINDOW: aHID.append( "FloatingWindow" ); break;
case RSC_MODALDIALOG: aHID.append( "ModalDialog" ); break;
case RSC_TABPAGE: aHID.append( "TabPage" ); break;
default: return OString();
}
}
else
{
// only controls with the following parents get auto help ids
const ImpRCStack *pRC1 = StackTop(1);
switch( pRC1->pResource->GetRT() ) {
case RSC_DOCKINGWINDOW:
case RSC_WORKWIN:
case RSC_MODELESSDIALOG:
case RSC_FLOATINGWINDOW:
case RSC_MODALDIALOG:
case RSC_TABPAGE:
// intentionally no breaks!
// auto help ids for controls
switch( pRC->pResource->GetRT() ) {
case RSC_TABCONTROL: aHID.append( "TabControl" ); break;
case RSC_RADIOBUTTON: aHID.append( "RadioButton" ); break;
case RSC_CHECKBOX: aHID.append( "CheckBox" ); break;
case RSC_TRISTATEBOX: aHID.append( "TriStateBox" ); break;
case RSC_EDIT: aHID.append( "Edit" ); break;
case RSC_MULTILINEEDIT: aHID.append( "MultiLineEdit" ); break;
case RSC_MULTILISTBOX: aHID.append( "MultiListBox" ); break;
case RSC_LISTBOX: aHID.append( "ListBox" ); break;
case RSC_COMBOBOX: aHID.append( "ComboBox" ); break;
case RSC_PUSHBUTTON: aHID.append( "PushButton" ); break;
case RSC_SPINFIELD: aHID.append( "SpinField" ); break;
case RSC_PATTERNFIELD: aHID.append( "PatternField" ); break;
case RSC_NUMERICFIELD: aHID.append( "NumericField" ); break;
case RSC_METRICFIELD: aHID.append( "MetricField" ); break;
case RSC_CURRENCYFIELD: aHID.append( "CurrencyField" ); break;
case RSC_DATEFIELD: aHID.append( "DateField" ); break;
case RSC_TIMEFIELD: aHID.append( "TimeField" ); break;
case RSC_NUMERICBOX: aHID.append( "NumericBox" ); break;
case RSC_METRICBOX: aHID.append( "MetricBox" ); break;
case RSC_CURRENCYBOX: aHID.append( "CurrencyBox" ); break;
case RSC_DATEBOX: aHID.append( "DateBox" ); break;
case RSC_TIMEBOX: aHID.append( "TimeBox" ); break;
case RSC_IMAGEBUTTON: aHID.append( "ImageButton" ); break;
case RSC_MENUBUTTON: aHID.append( "MenuButton" ); break;
default:
// no type, no auto HID
return OString();
}
break;
default:
return OString();
}
}
// append resource id hierarchy
for( int nOff = nCurStack-1; nOff >= 0; nOff-- )
{
aHID.append( '.' );
pRC = StackTop( nOff );
OSL_ENSURE( pRC->pResource, "missing resource in resource stack level !" );
if( pRC->pResource )
aHID.append( sal_Int32( pRC->pResource->GetId() ) );
}
return aHID.makeStringAndClear();
}
void ResMgr::SetReadStringHook( ResHookProc pProc )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
pImplResHookProc = pProc;
}
ResHookProc ResMgr::GetReadStringHook()
{
return pImplResHookProc;
}
void ResMgr::SetDefaultLocale( const LanguageTag& rLocale )
{
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
ResMgrContainer::get().setDefLocale( rLocale );
}
const OUString& ResMgr::GetFileName() const
{
return pImpRes->aFileName;
}
SimpleResMgr::SimpleResMgr( const sal_Char* pPrefixName,
const LanguageTag& rLocale )
{
OUString aPrefix( pPrefixName, strlen( pPrefixName ), osl_getThreadTextEncoding() );
LanguageTag aLocale( rLocale );
osl::Guard<osl::Mutex> aGuard( getResMgrMutex() );
if( aLocale.isSystemLocale() )
aLocale = ResMgrContainer::get().getDefLocale();
m_pResImpl = ResMgrContainer::get().getResMgr( aPrefix, aLocale, true );
DBG_ASSERT( m_pResImpl, "SimpleResMgr::SimpleResMgr : have no impl class !" );
}
SimpleResMgr::~SimpleResMgr()
{
delete m_pResImpl;
}
SimpleResMgr* SimpleResMgr::Create( const sal_Char* pPrefixName, LanguageTag aLocale )
{
return new SimpleResMgr( pPrefixName, aLocale );
}
bool SimpleResMgr::IsAvailable( RESOURCE_TYPE _resourceType, sal_uInt32 _resourceId )
{
osl::MutexGuard aGuard(m_aAccessSafety);
if ( ( RSC_STRING != _resourceType ) && ( RSC_RESOURCE != _resourceType ) )
return false;
DBG_ASSERT( m_pResImpl, "SimpleResMgr::IsAvailable: have no impl class !" );
return m_pResImpl->IsGlobalAvailable( _resourceType, _resourceId );
}
OUString SimpleResMgr::ReadString( sal_uInt32 nId )
{
osl::MutexGuard aGuard(m_aAccessSafety);
DBG_ASSERT( m_pResImpl, "SimpleResMgr::ReadString : have no impl class !" );
// perhaps constructed with an invalid filename ?
OUString sReturn;
if ( !m_pResImpl )
return sReturn;
void* pResHandle = NULL;
InternalResMgr* pFallback = m_pResImpl;
RSHEADER_TYPE* pResHeader = (RSHEADER_TYPE*)m_pResImpl->LoadGlobalRes( RSC_STRING, nId, &pResHandle );
if ( !pResHeader )
{
osl::Guard<osl::Mutex> aGuard2( getResMgrMutex() );
// try fallback
while( ! pResHandle && pFallback )
{
InternalResMgr* pOldFallback = pFallback;
pFallback = ResMgrContainer::get().getNextFallback( pFallback );
if( pOldFallback != m_pResImpl )
ResMgrContainer::get().freeResMgr( pOldFallback );
if( pFallback )
{
// handle possible recursion
if( pFallback->aLocale != m_pResImpl->aLocale )
{
pResHeader = (RSHEADER_TYPE*)pFallback->LoadGlobalRes( RSC_STRING, nId, &pResHandle );
}
else
{
ResMgrContainer::get().freeResMgr( pFallback );
pFallback = NULL;
}
}
}
if( ! pResHandle )
// no such resource
return sReturn;
}
// sal_uIntPtr nLen = pResHeader->GetLocalOff() - sizeof(RSHEADER_TYPE);
ResMgr::GetString( sReturn, (const sal_uInt8*)(pResHeader+1) );
// not necessary with te current implementation which holds the string table permanently, but to be sure ....
// note: pFallback cannot be NULL here and is either the fallback or m_pResImpl
InternalResMgr::FreeGlobalRes( pResHeader, pResHandle );
if( m_pResImpl != pFallback )
{
osl::Guard<osl::Mutex> aGuard2( getResMgrMutex() );
ResMgrContainer::get().freeResMgr( pFallback );
}
return sReturn;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */