tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / scripting / source / stringresource / stringresource.cxx
blobca9d55ba3f79de2d023971b85a616cd9a9182f33
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <memory>
21 #include "stringresource.hxx"
22 #include <com/sun/star/io/TempFile.hpp>
23 #include <com/sun/star/io/TextInputStream.hpp>
24 #include <com/sun/star/io/TextOutputStream.hpp>
25 #include <com/sun/star/io/XStream.hpp>
26 #include <com/sun/star/io/XSeekable.hpp>
27 #include <com/sun/star/embed/ElementModes.hpp>
28 #include <com/sun/star/lang/NoSupportException.hpp>
29 #include <com/sun/star/resource/MissingResourceException.hpp>
30 #include <cppuhelper/supportsservice.hxx>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/container/ElementExistException.hpp>
33 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
35 #include <osl/diagnose.h>
36 #include <o3tl/string_view.hxx>
37 #include <rtl/ref.hxx>
38 #include <rtl/tencinfo.h>
39 #include <rtl/ustrbuf.hxx>
40 #include <tools/urlobj.hxx>
41 #include <unotools/tempfile.hxx>
42 #include <i18nlangtag/languagetag.hxx>
43 #include <sal/log.hxx>
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::lang;
47 using namespace ::com::sun::star::uno;
48 using namespace ::com::sun::star::ucb;
49 using namespace ::com::sun::star::util;
50 using namespace ::com::sun::star::embed;
51 using namespace ::com::sun::star::container;
54 namespace stringresource
57 // StringResourceImpl
59 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
60 scripting_StringResourcePersistenceImpl_implementation(
61 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
63 return cppu::acquire(new StringResourcePersistenceImpl(context));
67 StringResourceImpl::StringResourceImpl( const Reference< XComponentContext >& rxContext )
68 : m_xContext( rxContext )
69 , m_pCurrentLocaleItem( nullptr )
70 , m_pDefaultLocaleItem( nullptr )
71 , m_bDefaultModified( false )
72 , m_bModified( false )
73 , m_bReadOnly( false )
74 , m_nNextUniqueNumericId( UNIQUE_NUMBER_NEEDS_INITIALISATION )
79 StringResourceImpl::~StringResourceImpl()
84 // XServiceInfo
86 OUString StringResourceImpl::getImplementationName( )
88 return u"com.sun.star.comp.scripting.StringResource"_ustr;
91 sal_Bool StringResourceImpl::supportsService( const OUString& rServiceName )
93 return cppu::supportsService(this, rServiceName);
96 Sequence< OUString > StringResourceImpl::getSupportedServiceNames( )
98 return { u"com.sun.star.resource.StringResource"_ustr };
102 // XModifyBroadcaster
104 void StringResourceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
106 if( !aListener.is() )
107 throw RuntimeException();
109 std::unique_lock aGuard( m_aMutex );
110 m_aListenerContainer.addInterface( aGuard, aListener );
113 void StringResourceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
115 if( !aListener.is() )
116 throw RuntimeException();
118 std::unique_lock aGuard( m_aMutex );
119 m_aListenerContainer.removeInterface( aGuard, aListener );
123 // XStringResourceResolver
125 OUString StringResourceImpl::implResolveString
126 (std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, LocaleItem* pLocaleItem)
128 OUString aRetStr;
129 bool bSuccess = false;
130 if( pLocaleItem != nullptr && loadLocale(rGuard, pLocaleItem) )
132 IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
133 if( it != pLocaleItem->m_aIdToStringMap.end() )
135 aRetStr = (*it).second;
136 bSuccess = true;
139 if( !bSuccess )
141 throw css::resource::MissingResourceException( "StringResourceImpl: No entry for ResourceID: " + ResourceID );
143 return aRetStr;
146 OUString StringResourceImpl::resolveString( const OUString& ResourceID )
148 std::unique_lock aGuard( m_aMutex );
149 return implResolveString(aGuard, ResourceID, m_pCurrentLocaleItem);
152 OUString StringResourceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
154 std::unique_lock aGuard( m_aMutex );
155 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
156 return implResolveString(aGuard, ResourceID, pLocaleItem);
159 bool StringResourceImpl::implHasEntryForId(std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, LocaleItem* pLocaleItem)
161 bool bSuccess = false;
162 if( pLocaleItem != nullptr && loadLocale(rGuard, pLocaleItem ) )
164 IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
165 if( it != pLocaleItem->m_aIdToStringMap.end() )
166 bSuccess = true;
168 return bSuccess;
171 sal_Bool StringResourceImpl::hasEntryForId( const OUString& ResourceID )
173 std::unique_lock aGuard( m_aMutex );
174 return implHasEntryForId(aGuard, ResourceID, m_pCurrentLocaleItem);
177 sal_Bool StringResourceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
178 const Locale& locale )
180 std::unique_lock aGuard( m_aMutex );
181 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
182 return implHasEntryForId(aGuard, ResourceID, pLocaleItem);
185 Sequence< OUString > StringResourceImpl::implGetResourceIDs(std::unique_lock<std::mutex>& rGuard, LocaleItem* pLocaleItem)
187 Sequence< OUString > aIDSeq( 0 );
188 if( pLocaleItem && loadLocale(rGuard, pLocaleItem) )
190 const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
191 sal_Int32 nResourceIDCount = rHashMap.size();
192 aIDSeq.realloc( nResourceIDCount );
193 OUString* pStrings = aIDSeq.getArray();
195 int iTarget = 0;
196 for( const auto& rEntry : rHashMap )
198 pStrings[iTarget] = rEntry.first;
199 iTarget++;
202 return aIDSeq;
205 Sequence< OUString > StringResourceImpl::getResourceIDsForLocale
206 ( const Locale& locale )
208 std::unique_lock aGuard( m_aMutex );
209 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
210 return implGetResourceIDs(aGuard, pLocaleItem);
213 Sequence< OUString > StringResourceImpl::getResourceIDs( )
215 std::unique_lock aGuard( m_aMutex );
216 return implGetResourceIDs(aGuard, m_pCurrentLocaleItem);
219 Locale StringResourceImpl::getCurrentLocale()
221 std::unique_lock aGuard( m_aMutex );
223 Locale aRetLocale;
224 if( m_pCurrentLocaleItem != nullptr )
225 aRetLocale = m_pCurrentLocaleItem->m_locale;
226 return aRetLocale;
229 Locale StringResourceImpl::getDefaultLocale( )
231 std::unique_lock aGuard( m_aMutex );
233 Locale aRetLocale;
234 if( m_pDefaultLocaleItem != nullptr )
235 aRetLocale = m_pDefaultLocaleItem->m_locale;
236 return aRetLocale;
239 Sequence< Locale > StringResourceImpl::getLocales( )
241 std::unique_lock aGuard( m_aMutex );
243 sal_Int32 nSize = m_aLocaleItemVector.size();
244 Sequence< Locale > aLocalSeq( nSize );
245 Locale* pLocales = aLocalSeq.getArray();
246 int iTarget = 0;
247 for( const auto& pLocaleItem : m_aLocaleItemVector )
249 pLocales[iTarget] = pLocaleItem->m_locale;
250 iTarget++;
252 return aLocalSeq;
256 // XStringResourceManager
258 void StringResourceImpl::implCheckReadOnly( const char* pExceptionMsg )
260 if( m_bReadOnly )
262 OUString errorMsg = OUString::createFromAscii( pExceptionMsg );
263 throw NoSupportException( errorMsg );
267 sal_Bool StringResourceImpl::isReadOnly()
269 return m_bReadOnly;
272 void StringResourceImpl::implSetCurrentLocale( std::unique_lock<std::mutex>& rGuard, const Locale& locale,
273 bool FindClosestMatch, bool bUseDefaultIfNoMatch )
275 LocaleItem* pLocaleItem = nullptr;
276 if( FindClosestMatch )
277 pLocaleItem = getClosestMatchItemForLocale( locale );
278 else
279 pLocaleItem = getItemForLocale( locale, true );
281 if( pLocaleItem == nullptr && bUseDefaultIfNoMatch )
282 pLocaleItem = m_pDefaultLocaleItem;
284 if( pLocaleItem != nullptr )
286 (void)loadLocale(rGuard, pLocaleItem);
287 m_pCurrentLocaleItem = pLocaleItem;
289 // Only notify without modifying
290 implNotifyListeners(rGuard);
294 void StringResourceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
296 std::unique_lock aGuard( m_aMutex );
297 implSetCurrentLocale( aGuard, locale, FindClosestMatch, false/*bUseDefaultIfNoMatch*/ );
300 void StringResourceImpl::setDefaultLocale( const Locale& locale )
302 std::unique_lock aGuard( m_aMutex );
303 implCheckReadOnly( "StringResourceImpl::setDefaultLocale(): Read only" );
305 LocaleItem* pLocaleItem = getItemForLocale( locale, true );
306 if( pLocaleItem && pLocaleItem != m_pDefaultLocaleItem )
308 if( m_pDefaultLocaleItem )
310 m_aChangedDefaultLocaleVector.push_back(
311 std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
314 m_pDefaultLocaleItem = pLocaleItem;
315 m_bDefaultModified = true;
316 implModified(aGuard);
320 void StringResourceImpl::implSetString( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID,
321 const OUString& Str, LocaleItem* pLocaleItem )
323 if( !(pLocaleItem != nullptr && loadLocale(rGuard, pLocaleItem)) )
324 return;
326 IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
328 IdToStringMap::iterator it = rHashMap.find( ResourceID );
329 bool bNew = ( it == rHashMap.end() );
330 if( bNew )
332 IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
333 rIndexMap[ ResourceID ] = pLocaleItem->m_nNextIndex++;
334 implScanIdForNumber(rGuard, ResourceID);
336 rHashMap[ ResourceID ] = Str;
337 pLocaleItem->m_bModified = true;
338 implModified(rGuard);
341 void StringResourceImpl::setString( const OUString& ResourceID, const OUString& Str )
343 std::unique_lock aGuard( m_aMutex );
344 implCheckReadOnly( "StringResourceImpl::setString(): Read only" );
345 implSetString( aGuard, ResourceID, Str, m_pCurrentLocaleItem );
348 void StringResourceImpl::setStringForLocale
349 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
351 std::unique_lock aGuard( m_aMutex );
352 implCheckReadOnly( "StringResourceImpl::setStringForLocale(): Read only" );
353 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
354 implSetString( aGuard, ResourceID, Str, pLocaleItem );
357 void StringResourceImpl::implRemoveId( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, LocaleItem* pLocaleItem )
359 if( pLocaleItem != nullptr && loadLocale(rGuard, pLocaleItem) )
361 IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
362 IdToStringMap::iterator it = rHashMap.find( ResourceID );
363 if( it == rHashMap.end() )
365 throw css::resource::MissingResourceException( "StringResourceImpl: No entries for ResourceID: " + ResourceID );
367 rHashMap.erase( it );
368 pLocaleItem->m_bModified = true;
369 implModified(rGuard);
373 void StringResourceImpl::removeId( const OUString& ResourceID )
375 std::unique_lock aGuard( m_aMutex );
376 implCheckReadOnly( "StringResourceImpl::removeId(): Read only" );
377 implRemoveId( aGuard, ResourceID, m_pCurrentLocaleItem );
380 void StringResourceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
382 std::unique_lock aGuard( m_aMutex );
383 implCheckReadOnly( "StringResourceImpl::removeIdForLocale(): Read only" );
384 LocaleItem* pLocaleItem = getItemForLocale( locale, false );
385 implRemoveId( aGuard, ResourceID, pLocaleItem );
388 void StringResourceImpl::newLocale( const Locale& locale )
390 std::unique_lock aGuard( m_aMutex );
391 implCheckReadOnly( "StringResourceImpl::newLocale(): Read only" );
393 if( getItemForLocale( locale, false ) != nullptr )
395 throw ElementExistException( u"StringResourceImpl: locale already exists"_ustr );
398 // TODO?: Check if locale is valid? How?
399 //if (!bValid)
401 // OUString errorMsg("StringResourceImpl: Invalid locale");
402 // throw IllegalArgumentException( errorMsg, Reference< XInterface >(), 0 );
405 LocaleItem* pLocaleItem = new LocaleItem( locale );
406 m_aLocaleItemVector.emplace_back( pLocaleItem );
407 pLocaleItem->m_bModified = true;
409 // Copy strings from default locale
410 LocaleItem* pCopyFromItem = m_pDefaultLocaleItem;
411 if( pCopyFromItem == nullptr )
412 pCopyFromItem = m_pCurrentLocaleItem;
413 if( pCopyFromItem != nullptr && loadLocale(aGuard, pCopyFromItem) )
415 const IdToStringMap& rSourceMap = pCopyFromItem->m_aIdToStringMap;
416 IdToStringMap& rTargetMap = pLocaleItem->m_aIdToStringMap;
417 for( const auto& rEntry : rSourceMap )
419 OUString aId = rEntry.first;
420 rTargetMap[ aId ] = rEntry.second;
423 const IdToIndexMap& rSourceIndexMap = pCopyFromItem->m_aIdToIndexMap;
424 IdToIndexMap& rTargetIndexMap = pLocaleItem->m_aIdToIndexMap;
425 for( const auto& rIndex : rSourceIndexMap )
427 OUString aId = rIndex.first;
428 sal_Int32 nIndex = rIndex.second;
429 rTargetIndexMap[ aId ] = nIndex;
431 pLocaleItem->m_nNextIndex = pCopyFromItem->m_nNextIndex;
434 if( m_pCurrentLocaleItem == nullptr )
435 m_pCurrentLocaleItem = pLocaleItem;
437 if( m_pDefaultLocaleItem == nullptr )
439 m_pDefaultLocaleItem = pLocaleItem;
440 m_bDefaultModified = true;
443 implModified(aGuard);
446 void StringResourceImpl::removeLocale( const Locale& locale )
448 std::unique_lock aGuard( m_aMutex );
449 implCheckReadOnly( "StringResourceImpl::removeLocale(): Read only" );
451 LocaleItem* pRemoveItem = getItemForLocale( locale, true );
452 if( !pRemoveItem )
453 return;
455 // Last locale?
456 sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
457 if( nLocaleCount > 1 )
459 if( m_pCurrentLocaleItem == pRemoveItem ||
460 m_pDefaultLocaleItem == pRemoveItem )
462 LocaleItem* pFallbackItem = nullptr;
463 for( const auto& pLocaleItem : m_aLocaleItemVector )
465 if( pLocaleItem.get() != pRemoveItem )
467 pFallbackItem = pLocaleItem.get();
468 break;
471 if( m_pCurrentLocaleItem == pRemoveItem )
473 assert(pFallbackItem);
474 setCurrentLocale( pFallbackItem->m_locale, false/*FindClosestMatch*/ );
476 if( m_pDefaultLocaleItem == pRemoveItem )
478 assert(pFallbackItem);
479 setDefaultLocale( pFallbackItem->m_locale );
483 auto it = std::find_if(m_aLocaleItemVector.begin(), m_aLocaleItemVector.end(),
484 [&pRemoveItem](const std::unique_ptr<LocaleItem>& rxItem) { return rxItem.get() == pRemoveItem; });
485 if (it == m_aLocaleItemVector.end())
486 return;
488 // Remember locale item to delete file while storing
489 m_aDeletedLocaleItemVector.push_back( std::move(*it) );
491 // Last locale?
492 if( nLocaleCount == 1 )
494 m_nNextUniqueNumericId = 0;
495 if( m_pDefaultLocaleItem )
497 m_aChangedDefaultLocaleVector.push_back(
498 std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
500 m_pCurrentLocaleItem = nullptr;
501 m_pDefaultLocaleItem = nullptr;
504 m_aLocaleItemVector.erase( it );
506 implModified(aGuard);
509 void StringResourceImpl::implScanIdForNumber(std::unique_lock<std::mutex>& /*rGuard*/, const OUString& ResourceID)
511 const sal_Unicode* pSrc = ResourceID.getStr();
512 sal_Int32 nLen = ResourceID.getLength();
514 sal_Int32 nNumber = 0;
515 for( sal_Int32 i = 0 ; i < nLen ; i++ )
517 sal_Unicode c = pSrc[i];
518 if( c >= '0' && c <= '9' )
520 sal_uInt16 nDigitVal = c - '0';
521 nNumber = 10*nNumber + nDigitVal;
523 else
524 break;
527 if( m_nNextUniqueNumericId < nNumber + 1 )
528 m_nNextUniqueNumericId = nNumber + 1;
531 sal_Int32 StringResourceImpl::getUniqueNumericId( )
533 std::unique_lock aGuard( m_aMutex );
535 if( m_nNextUniqueNumericId == UNIQUE_NUMBER_NEEDS_INITIALISATION )
537 implLoadAllLocales(aGuard);
538 m_nNextUniqueNumericId = 0;
541 if( m_nNextUniqueNumericId < UNIQUE_NUMBER_NEEDS_INITIALISATION )
543 throw NoSupportException( u"getUniqueNumericId: Extended sal_Int32 range"_ustr );
545 return m_nNextUniqueNumericId;
549 // Private helper methods
551 LocaleItem* StringResourceImpl::getItemForLocale
552 ( const Locale& locale, bool bException )
554 LocaleItem* pRetItem = nullptr;
556 // Search for locale
557 for( auto& pLocaleItem : m_aLocaleItemVector )
559 if( pLocaleItem )
561 Locale& cmp_locale = pLocaleItem->m_locale;
562 if( cmp_locale.Language == locale.Language &&
563 cmp_locale.Country == locale.Country &&
564 cmp_locale.Variant == locale.Variant )
566 pRetItem = pLocaleItem.get();
567 break;
572 if( pRetItem == nullptr && bException )
574 throw IllegalArgumentException( u"StringResourceImpl: Invalid locale"_ustr, Reference< XInterface >(), 0 );
576 return pRetItem;
579 // Returns the LocaleItem for a given locale, if it exists, otherwise NULL.
580 // This method performs a closest match search, at least the language must match.
581 LocaleItem* StringResourceImpl::getClosestMatchItemForLocale( const Locale& locale )
583 LocaleItem* pRetItem = nullptr;
585 ::std::vector< Locale > aLocales( m_aLocaleItemVector.size());
586 size_t i = 0;
587 for( const auto& pLocaleItem : m_aLocaleItemVector )
589 aLocales[i] = (pLocaleItem ? pLocaleItem->m_locale : Locale());
590 ++i;
592 ::std::vector< Locale >::const_iterator iFound( LanguageTag::getMatchingFallback( aLocales, locale));
593 if (iFound != aLocales.end())
594 pRetItem = (m_aLocaleItemVector.begin() + (iFound - aLocales.begin()))->get();
596 return pRetItem;
599 void StringResourceImpl::implModified(std::unique_lock<std::mutex>& rGuard)
601 m_bModified = true;
602 implNotifyListeners(rGuard);
605 void StringResourceImpl::implNotifyListeners(std::unique_lock<std::mutex>& rGuard)
607 EventObject aEvent;
608 aEvent.Source = getXWeak();
609 m_aListenerContainer.forEach(rGuard,
610 [&aEvent](const css::uno::Reference<XModifyListener>& xListener)
612 xListener->modified(aEvent);
617 // Loading
619 bool StringResourceImpl::loadLocale(std::unique_lock<std::mutex>& /*rGuard*/, LocaleItem*)
621 // Base implementation has nothing to load
622 return true;
625 void StringResourceImpl::implLoadAllLocales(std::unique_lock<std::mutex>& /*rGuard*/)
627 // Base implementation has nothing to load
630 // StringResourcePersistenceImpl
632 StringResourcePersistenceImpl::StringResourcePersistenceImpl( const Reference< XComponentContext >& rxContext )
633 : StringResourcePersistenceImpl_BASE( rxContext )
637 StringResourcePersistenceImpl::~StringResourcePersistenceImpl()
641 // XServiceInfo
643 OUString StringResourcePersistenceImpl::getImplementationName( )
645 return u"com.sun.star.comp.scripting.StringResource"_ustr;
649 sal_Bool StringResourcePersistenceImpl::supportsService( const OUString& rServiceName )
651 return cppu::supportsService( this, rServiceName );
655 Sequence< OUString > StringResourcePersistenceImpl::getSupportedServiceNames( )
657 return StringResourceImpl::getSupportedServiceNames();
661 // XInitialization base functionality for derived classes
664 constexpr OUString aNameBaseDefaultStr = u"strings"_ustr;
666 void StringResourcePersistenceImpl::implInitializeCommonParameters
667 ( std::unique_lock<std::mutex>& rGuard, const Sequence< Any >& aArguments )
669 bool bReadOnlyOk = (aArguments[1] >>= m_bReadOnly);
670 if( !bReadOnlyOk )
672 throw IllegalArgumentException( u"XInitialization::initialize: Expected ReadOnly flag"_ustr, Reference< XInterface >(), 1 );
675 css::lang::Locale aCurrentLocale;
676 bool bLocaleOk = (aArguments[2] >>= aCurrentLocale);
677 if( !bLocaleOk )
679 throw IllegalArgumentException( u"XInitialization::initialize: Expected Locale"_ustr, Reference< XInterface >(), 2 );
682 bool bNameBaseOk = (aArguments[3] >>= m_aNameBase);
683 if( !bNameBaseOk )
685 throw IllegalArgumentException( u"XInitialization::initialize: Expected NameBase string"_ustr, Reference< XInterface >(), 3 );
687 if( m_aNameBase.isEmpty() )
688 m_aNameBase = aNameBaseDefaultStr;
690 bool bCommentOk = (aArguments[4] >>= m_aComment);
691 if( !bCommentOk )
693 throw IllegalArgumentException( u"XInitialization::initialize: Expected Comment string"_ustr, Reference< XInterface >(), 4 );
696 implScanLocales(rGuard);
698 implSetCurrentLocale( rGuard, aCurrentLocale, true/*FindClosestMatch*/, true/*bUseDefaultIfNoMatch*/ );
702 // Forwarding calls to base class
704 // XModifyBroadcaster
705 void StringResourcePersistenceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
707 StringResourceImpl::addModifyListener( aListener );
709 void StringResourcePersistenceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
711 StringResourceImpl::removeModifyListener( aListener );
714 // XStringResourceResolver
715 OUString StringResourcePersistenceImpl::resolveString( const OUString& ResourceID )
717 return StringResourceImpl::resolveString( ResourceID ) ;
719 OUString StringResourcePersistenceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
721 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
723 sal_Bool StringResourcePersistenceImpl::hasEntryForId( const OUString& ResourceID )
725 return StringResourceImpl::hasEntryForId( ResourceID ) ;
727 sal_Bool StringResourcePersistenceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
728 const Locale& locale )
730 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
732 Locale StringResourcePersistenceImpl::getCurrentLocale()
734 return StringResourceImpl::getCurrentLocale();
736 Locale StringResourcePersistenceImpl::getDefaultLocale( )
738 return StringResourceImpl::getDefaultLocale();
740 Sequence< Locale > StringResourcePersistenceImpl::getLocales( )
742 return StringResourceImpl::getLocales();
745 // XStringResourceManager
746 sal_Bool StringResourcePersistenceImpl::isReadOnly()
748 return StringResourceImpl::isReadOnly();
750 void StringResourcePersistenceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
752 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
754 void StringResourcePersistenceImpl::setDefaultLocale( const Locale& locale )
756 StringResourceImpl::setDefaultLocale( locale );
758 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDs( )
760 return StringResourceImpl::getResourceIDs();
762 void StringResourcePersistenceImpl::setString( const OUString& ResourceID, const OUString& Str )
764 StringResourceImpl::setString( ResourceID, Str );
766 void StringResourcePersistenceImpl::setStringForLocale
767 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
769 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
771 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDsForLocale
772 ( const Locale& locale )
774 return StringResourceImpl::getResourceIDsForLocale( locale );
776 void StringResourcePersistenceImpl::removeId( const OUString& ResourceID )
778 StringResourceImpl::removeId( ResourceID );
780 void StringResourcePersistenceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
782 StringResourceImpl::removeIdForLocale( ResourceID, locale );
784 void StringResourcePersistenceImpl::newLocale( const Locale& locale )
786 StringResourceImpl::newLocale( locale );
788 void StringResourcePersistenceImpl::removeLocale( const Locale& locale )
790 StringResourceImpl::removeLocale( locale );
792 sal_Int32 StringResourcePersistenceImpl::getUniqueNumericId( )
794 return StringResourceImpl::getUniqueNumericId();
798 // XStringResourcePersistence
800 void StringResourcePersistenceImpl::store()
804 sal_Bool StringResourcePersistenceImpl::isModified( )
806 std::unique_lock aGuard( m_aMutex );
808 return m_bModified;
811 void StringResourcePersistenceImpl::setComment( const OUString& Comment )
813 m_aComment = Comment;
816 void StringResourcePersistenceImpl::storeToStorage( const Reference< XStorage >& Storage,
817 const OUString& NameBase, const OUString& Comment )
819 std::unique_lock aGuard( m_aMutex );
821 implStoreAtStorage(aGuard, NameBase, Comment, Storage, false/*bUsedForStore*/, true/*bStoreAll*/ );
824 void StringResourcePersistenceImpl::implStoreAtStorage
826 std::unique_lock<std::mutex>& rGuard,
827 const OUString& aNameBase,
828 const OUString& aComment,
829 const Reference< css::embed::XStorage >& Storage,
830 bool bUsedForStore,
831 bool bStoreAll
834 // Delete files for deleted locales
835 if( bUsedForStore )
837 for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
839 if( pLocaleItem )
841 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".properties";
845 Storage->removeElement( aStreamName );
847 catch( Exception& )
850 pLocaleItem.reset();
853 m_aDeletedLocaleItemVector.clear();
856 for( auto& pLocaleItem : m_aLocaleItemVector )
858 if( pLocaleItem != nullptr && (bStoreAll || pLocaleItem->m_bModified) &&
859 loadLocale(rGuard, pLocaleItem.get()) )
861 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), aNameBase ) + ".properties";
863 Reference< io::XStream > xElementStream =
864 Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
866 uno::Reference< beans::XPropertySet > xProps( xElementStream, uno::UNO_QUERY );
867 OSL_ENSURE( xProps.is(), "The StorageStream must implement XPropertySet interface!" );
868 if ( xProps.is() )
870 OUString aPropName(u"MediaType"_ustr);
871 xProps->setPropertyValue( aPropName, uno::Any( u"text/plain"_ustr ) );
873 aPropName = "UseCommonStoragePasswordEncryption";
874 xProps->setPropertyValue( aPropName, uno::Any( true ) );
877 Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
878 if( xOutputStream.is() )
879 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
880 xOutputStream->closeOutput();
882 if( bUsedForStore )
883 pLocaleItem->m_bModified = false;
887 // Delete files for changed defaults
888 if( bUsedForStore )
890 for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
892 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".default";
896 Storage->removeElement( aStreamName );
898 catch( Exception& )
901 pLocaleItem.reset();
903 m_aChangedDefaultLocaleVector.clear();
906 // Default locale
907 if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || m_bDefaultModified)) )
908 return;
910 OUString aStreamName = implGetFileNameForLocaleItem( m_pDefaultLocaleItem, aNameBase ) + ".default";
912 Reference< io::XStream > xElementStream =
913 Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
915 // Only create stream without content
916 Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
917 xOutputStream->closeOutput();
919 if( bUsedForStore )
920 m_bDefaultModified = false;
923 void StringResourcePersistenceImpl::storeToURL( const OUString& URL,
924 const OUString& NameBase, const OUString& Comment,
925 const Reference< css::task::XInteractionHandler >& Handler )
927 std::unique_lock aGuard( m_aMutex );
929 Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create(m_xContext);
930 if( Handler.is() )
931 xFileAccess->setInteractionHandler( Handler );
933 implStoreAtLocation(aGuard, URL, NameBase, Comment, xFileAccess, false/*bUsedForStore*/, true/*bStoreAll*/);
936 void StringResourcePersistenceImpl::implKillRemovedLocaleFiles
938 std::u16string_view Location,
939 const OUString& aNameBase,
940 const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
943 // Delete files for deleted locales
944 for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
946 if( pLocaleItem )
948 OUString aCompleteFileName =
949 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
950 if( xFileAccess->exists( aCompleteFileName ) )
951 xFileAccess->kill( aCompleteFileName );
953 pLocaleItem.reset();
956 m_aDeletedLocaleItemVector.clear();
959 void StringResourcePersistenceImpl::implKillChangedDefaultFiles
961 std::u16string_view Location,
962 const OUString& aNameBase,
963 const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
966 // Delete files for changed defaults
967 for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
969 OUString aCompleteFileName =
970 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location, true );
971 if( xFileAccess->exists( aCompleteFileName ) )
972 xFileAccess->kill( aCompleteFileName );
973 pLocaleItem.reset();
975 m_aChangedDefaultLocaleVector.clear();
978 void StringResourcePersistenceImpl::implStoreAtLocation
980 std::unique_lock<std::mutex>& rGuard,
981 std::u16string_view Location,
982 const OUString& aNameBase,
983 const OUString& aComment,
984 const Reference< ucb::XSimpleFileAccess3 >& xFileAccess,
985 bool bUsedForStore,
986 bool bStoreAll,
987 bool bKillAll
990 // Delete files for deleted locales
991 if( bUsedForStore || bKillAll )
992 implKillRemovedLocaleFiles( Location, aNameBase, xFileAccess );
994 for( auto& pLocaleItem : m_aLocaleItemVector )
996 if( pLocaleItem != nullptr && (bStoreAll || bKillAll || pLocaleItem->m_bModified) &&
997 loadLocale(rGuard, pLocaleItem.get()) )
999 OUString aCompleteFileName =
1000 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
1001 if( xFileAccess->exists( aCompleteFileName ) )
1002 xFileAccess->kill( aCompleteFileName );
1004 if( !bKillAll )
1006 // Create Output stream
1007 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1008 if( xOutputStream.is() )
1010 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
1011 xOutputStream->closeOutput();
1013 if( bUsedForStore )
1014 pLocaleItem->m_bModified = false;
1019 // Delete files for changed defaults
1020 if( bUsedForStore || bKillAll )
1021 implKillChangedDefaultFiles( Location, aNameBase, xFileAccess );
1023 // Default locale
1024 if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || bKillAll || m_bDefaultModified)) )
1025 return;
1027 OUString aCompleteFileName =
1028 implGetPathForLocaleItem( m_pDefaultLocaleItem, aNameBase, Location, true );
1029 if( xFileAccess->exists( aCompleteFileName ) )
1030 xFileAccess->kill( aCompleteFileName );
1032 if( !bKillAll )
1034 // Create Output stream
1035 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1036 if( xOutputStream.is() )
1037 xOutputStream->closeOutput();
1039 if( bUsedForStore )
1040 m_bDefaultModified = false;
1045 // BinaryOutput, helper class for exportBinary
1047 class BinaryOutput
1049 rtl::Reference< utl::TempFileFastService > m_xTempFile;
1051 public:
1052 explicit BinaryOutput();
1054 Reference< io::XOutputStream > getOutputStream() const
1055 { return m_xTempFile; }
1057 Sequence< ::sal_Int8 > closeAndGetData();
1059 // Template to be used with sal_Int16 and sal_Unicode
1060 template< class T >
1061 void write16BitInt( T n );
1062 void writeInt16( sal_Int16 n )
1063 { write16BitInt( n ); }
1064 void writeUnicodeChar( sal_Unicode n )
1065 { write16BitInt( n ); }
1066 void writeInt32( sal_Int32 n );
1067 void writeString( const OUString& aStr );
1070 BinaryOutput::BinaryOutput()
1072 m_xTempFile = new utl::TempFileFastService;
1075 template< class T >
1076 void BinaryOutput::write16BitInt( T n )
1078 if( !m_xTempFile.is() )
1079 return;
1081 Sequence< sal_Int8 > aSeq( 2 );
1082 sal_Int8* p = aSeq.getArray();
1084 sal_Int8 nLow = sal_Int8( n & 0xff );
1085 sal_Int8 nHigh = sal_Int8( n >> 8 );
1087 p[0] = nLow;
1088 p[1] = nHigh;
1089 m_xTempFile->writeBytes( aSeq );
1092 void BinaryOutput::writeInt32( sal_Int32 n )
1094 if( !m_xTempFile.is() )
1095 return;
1097 Sequence< sal_Int8 > aSeq( 4 );
1098 sal_Int8* p = aSeq.getArray();
1100 for( sal_Int16 i = 0 ; i < 4 ; i++ )
1102 p[i] = sal_Int8( n & 0xff );
1103 n >>= 8;
1105 m_xTempFile->writeBytes( aSeq );
1108 void BinaryOutput::writeString( const OUString& aStr )
1110 sal_Int32 nLen = aStr.getLength();
1111 const sal_Unicode* pStr = aStr.getStr();
1113 for( sal_Int32 i = 0 ; i < nLen ; i++ )
1114 writeUnicodeChar( pStr[i] );
1116 writeUnicodeChar( 0 );
1119 Sequence< ::sal_Int8 > BinaryOutput::closeAndGetData()
1121 Sequence< ::sal_Int8 > aRetSeq;
1122 if( !m_xTempFile.is() )
1123 return aRetSeq;
1125 m_xTempFile->closeOutput();
1127 sal_Int32 nSize = static_cast<sal_Int32>(m_xTempFile->getPosition());
1129 m_xTempFile->seek( 0 );
1130 sal_Int32 nRead = m_xTempFile->readBytes( aRetSeq, nSize );
1131 OSL_ENSURE( nRead == nSize, "BinaryOutput::closeAndGetData: nRead != nSize" );
1133 return aRetSeq;
1137 // Binary format:
1139 // Header
1140 // Byte Content
1141 // 0 + 1 sal_Int16: Version, currently 0, low byte first
1142 // 2 + 3 sal_Int16: Locale count = n, low byte first
1143 // 4 + 5 sal_Int16: Default Locale position in Locale list, == n if none
1144 // 6 - 7 sal_Int32: Start index locale block 0, lowest byte first
1145 // (n-1) * sal_Int32: Start index locale block 1 to n, lowest byte first
1146 // 6 + 4*n sal_Int32: "Start index" non existing locale block n+1,
1147 // marks the first invalid index, kind of EOF
1149 // Locale block
1150 // All strings are stored as 2-Byte-0 terminated sequence
1151 // of 16 bit Unicode characters, each with low byte first
1152 // Empty strings only contain the 2-Byte-0
1154 // Members of com.sun.star.lang.Locale
1155 // with l1 = Locale.Language.getLength()
1156 // with l2 = Locale.Country.getLength()
1157 // with l3 = Locale.Variant.getLength()
1158 // pos0 = 0 Locale.Language
1159 // pos1 = 2 * (l1 + 1) Locale.Country
1160 // pos2 = pos1 + 2 * (l2 + 1) Locale.Variant
1161 // pos3 = pos2 + 2 * (l3 + 1)
1162 // pos3 Properties file written by implWritePropertiesFile
1164 Sequence< sal_Int8 > StringResourcePersistenceImpl::exportBinary( )
1166 std::unique_lock aGuard( m_aMutex );
1168 BinaryOutput aOut;
1170 sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
1171 std::vector<Sequence< sal_Int8 >> aLocaleDataSeq(nLocaleCount);
1173 sal_Int32 iLocale = 0;
1174 sal_Int32 iDefault = 0;
1175 for( auto& pLocaleItem : m_aLocaleItemVector )
1177 if( pLocaleItem != nullptr && loadLocale(aGuard, pLocaleItem.get()) )
1179 if( m_pDefaultLocaleItem == pLocaleItem.get() )
1180 iDefault = iLocale;
1182 BinaryOutput aLocaleOut;
1183 implWriteLocaleBinary( pLocaleItem.get(), aLocaleOut );
1185 aLocaleDataSeq[iLocale] = aLocaleOut.closeAndGetData();
1187 ++iLocale;
1190 // Write header
1191 sal_Int16 nLocaleCount16 = static_cast<sal_Int16>(nLocaleCount);
1192 sal_Int16 iDefault16 = static_cast<sal_Int16>(iDefault);
1193 aOut.writeInt16( 0 ); // nVersion
1194 aOut.writeInt16( nLocaleCount16 );
1195 aOut.writeInt16( iDefault16 );
1197 // Write data positions
1198 sal_Int32 nDataPos = 6 + 4 * (nLocaleCount + 1);
1199 for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1201 aOut.writeInt32( nDataPos );
1203 Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1204 sal_Int32 nSeqLen = rSeq.getLength();
1205 nDataPos += nSeqLen;
1207 // Write final position
1208 aOut.writeInt32( nDataPos );
1210 // Write data
1211 Reference< io::XOutputStream > xOutputStream = aOut.getOutputStream();
1212 if( xOutputStream.is() )
1214 for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1216 Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1217 xOutputStream->writeBytes( rSeq );
1221 Sequence< sal_Int8 > aRetSeq = aOut.closeAndGetData();
1222 return aRetSeq;
1225 void StringResourcePersistenceImpl::implWriteLocaleBinary
1226 ( LocaleItem* pLocaleItem, BinaryOutput& rOut )
1228 Reference< io::XOutputStream > xOutputStream = rOut.getOutputStream();
1229 if( !xOutputStream.is() )
1230 return;
1232 Locale& rLocale = pLocaleItem->m_locale;
1233 rOut.writeString( rLocale.Language );
1234 rOut.writeString( rLocale.Country );
1235 rOut.writeString( rLocale.Variant );
1236 implWritePropertiesFile( pLocaleItem, xOutputStream, m_aComment );
1240 // BinaryOutput, helper class for exportBinary
1242 namespace {
1244 class BinaryInput
1246 Sequence< sal_Int8 > m_aData;
1248 const sal_Int8* m_pData;
1249 sal_Int32 m_nCurPos;
1250 sal_Int32 m_nSize;
1252 public:
1253 BinaryInput( const Sequence< ::sal_Int8 >& aData );
1255 Reference< io::XInputStream > getInputStreamForSection( sal_Int32 nSize );
1257 void seek( sal_Int32 nPos );
1258 sal_Int32 getPosition() const
1259 { return m_nCurPos; }
1261 sal_Int16 readInt16();
1262 sal_Int32 readInt32();
1263 sal_Unicode readUnicodeChar();
1264 OUString readString();
1269 BinaryInput::BinaryInput( const Sequence< ::sal_Int8 >& aData )
1270 : m_aData( aData )
1272 m_pData = m_aData.getConstArray();
1273 m_nCurPos = 0;
1274 m_nSize = m_aData.getLength();
1277 Reference< io::XInputStream > BinaryInput::getInputStreamForSection( sal_Int32 nSize )
1279 Reference< io::XInputStream > xIn;
1280 if( m_nCurPos + nSize <= m_nSize )
1282 rtl::Reference< utl::TempFileFastService > xTempOut = new utl::TempFileFastService;
1283 Sequence< sal_Int8 > aSection( m_pData + m_nCurPos, nSize );
1284 xTempOut->writeBytes( aSection );
1285 xTempOut->seek( 0 );
1286 xIn = xTempOut;
1288 else
1289 OSL_FAIL( "BinaryInput::getInputStreamForSection(): Read past end" );
1291 return xIn;
1294 void BinaryInput::seek( sal_Int32 nPos )
1296 if( nPos <= m_nSize )
1297 m_nCurPos = nPos;
1298 else
1299 OSL_FAIL( "BinaryInput::seek(): Position past end" );
1303 sal_Int16 BinaryInput::readInt16()
1305 sal_Int16 nRet = 0;
1306 if( m_nCurPos + 2 <= m_nSize )
1308 nRet = nRet + sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1309 nRet += 256 * sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1311 else
1312 OSL_FAIL( "BinaryInput::readInt16(): Read past end" );
1314 return nRet;
1317 sal_Int32 BinaryInput::readInt32()
1319 sal_Int32 nRet = 0;
1320 if( m_nCurPos + 4 <= m_nSize )
1322 sal_Int32 nFactor = 1;
1323 for( sal_Int16 i = 0; i < 4; i++ )
1325 nRet += sal_uInt8( m_pData[m_nCurPos++] ) * nFactor;
1326 nFactor *= 256;
1329 else
1330 OSL_FAIL( "BinaryInput::readInt32(): Read past end" );
1332 return nRet;
1335 sal_Unicode BinaryInput::readUnicodeChar()
1337 sal_uInt16 nRet = 0;
1338 if( m_nCurPos + 2 <= m_nSize )
1340 nRet = nRet + sal_uInt8( m_pData[m_nCurPos++] );
1341 nRet += 256 * sal_uInt8( m_pData[m_nCurPos++] );
1343 else
1344 OSL_FAIL( "BinaryInput::readUnicodeChar(): Read past end" );
1346 sal_Unicode cRet = nRet;
1347 return cRet;
1350 OUString BinaryInput::readString()
1352 OUStringBuffer aBuf;
1353 sal_Unicode c;
1356 c = readUnicodeChar();
1357 if( c != 0 )
1358 aBuf.append( c );
1360 while( c != 0 );
1362 OUString aRetStr = aBuf.makeStringAndClear();
1363 return aRetStr;
1366 void StringResourcePersistenceImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
1368 // Init: Remove all locales
1369 sal_Int32 nOldLocaleCount = 0;
1372 Sequence< Locale > aLocaleSeq = getLocales();
1373 nOldLocaleCount = aLocaleSeq.getLength();
1374 if( nOldLocaleCount > 0 )
1376 removeLocale( aLocaleSeq[0] );
1379 while( nOldLocaleCount > 0 );
1381 // Import data
1382 BinaryInput aIn( Data );
1384 aIn.readInt16(); // version
1385 sal_Int32 nLocaleCount = aIn.readInt16();
1386 sal_Int32 iDefault = aIn.readInt16();
1388 std::unique_ptr<sal_Int32[]> pPositions( new sal_Int32[nLocaleCount + 1] );
1389 for( sal_Int32 i = 0; i < nLocaleCount + 1; i++ )
1390 pPositions[i] = aIn.readInt32();
1392 // Import locales
1393 LocaleItem* pUseAsDefaultItem = nullptr;
1394 for( sal_Int32 i = 0; i < nLocaleCount; i++ )
1396 sal_Int32 nPos = pPositions[i];
1397 aIn.seek( nPos );
1399 Locale aLocale;
1400 aLocale.Language = aIn.readString();
1401 aLocale.Country = aIn.readString();
1402 aLocale.Variant = aIn.readString();
1404 sal_Int32 nAfterStringPos = aIn.getPosition();
1405 sal_Int32 nSize = pPositions[i+1] - nAfterStringPos;
1406 Reference< io::XInputStream > xInput = aIn.getInputStreamForSection( nSize );
1407 if( xInput.is() )
1409 LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale) );
1410 if( iDefault == i )
1411 pUseAsDefaultItem = pLocaleItem;
1412 std::unique_lock aGuard( m_aMutex );
1413 m_aLocaleItemVector.emplace_back( pLocaleItem );
1414 implReadPropertiesFile(aGuard, pLocaleItem, xInput);
1418 if( pUseAsDefaultItem != nullptr )
1419 setDefaultLocale( pUseAsDefaultItem->m_locale );
1423 // Private helper methods
1425 static bool checkNamingSceme( std::u16string_view aName, std::u16string_view aNameBase,
1426 Locale& aLocale )
1428 bool bSuccess = false;
1430 size_t nNameLen = aName.size();
1431 size_t nNameBaseLen = aNameBase.size();
1433 // Name has to start with NameBase followed
1434 // by a '_' and at least one more character
1435 if( o3tl::starts_with(aName, aNameBase) && nNameBaseLen < nNameLen-1 &&
1436 aName[nNameBaseLen] == '_' )
1438 bSuccess = true;
1440 /* FIXME-BCP47: this uses '_' underscore character as separator and
1441 * also appends Variant, which can't be blindly changed as it would
1442 * violate the naming scheme in use. */
1444 sal_Int32 iStart = nNameBaseLen + 1;
1445 size_t iNext_ = aName.find( '_', iStart );
1446 if( iNext_ != std::u16string_view::npos && iNext_ < nNameLen-1 )
1448 aLocale.Language = aName.substr( iStart, iNext_ - iStart );
1450 iStart = iNext_ + 1;
1451 iNext_ = aName.find( '_', iStart );
1452 if( iNext_ != std::u16string_view::npos && iNext_ < nNameLen-1 )
1454 aLocale.Country = aName.substr( iStart, iNext_ - iStart );
1455 aLocale.Variant = aName.substr( iNext_ + 1 );
1457 else
1458 aLocale.Country = aName.substr( iStart );
1460 else
1461 aLocale.Language = aName.substr( iStart );
1463 return bSuccess;
1466 void StringResourcePersistenceImpl::implLoadAllLocales(std::unique_lock<std::mutex>& rGuard)
1468 for( auto& pLocaleItem : m_aLocaleItemVector )
1469 if( pLocaleItem )
1470 loadLocale(rGuard, pLocaleItem.get());
1473 // Scan locale properties files helper
1474 void StringResourcePersistenceImpl::implScanLocaleNames( const Sequence< OUString >& aContentSeq )
1476 Locale aDefaultLocale;
1477 bool bDefaultFound = false;
1479 for( const OUString& aCompleteName : aContentSeq )
1481 OUString aPureName;
1482 OUString aExtension;
1483 sal_Int32 iDot = aCompleteName.lastIndexOf( '.' );
1484 sal_Int32 iSlash = aCompleteName.lastIndexOf( '/' );
1485 if( iDot != -1 && iDot > iSlash)
1487 sal_Int32 iCopyFrom = (iSlash != -1) ? iSlash + 1 : 0;
1488 aPureName = aCompleteName.copy( iCopyFrom, iDot-iCopyFrom );
1489 aExtension = aCompleteName.copy( iDot + 1 );
1492 if ( aExtension == "properties" )
1494 //OUString aName = aInetObj.getBase();
1495 Locale aLocale;
1497 if( checkNamingSceme( aPureName, m_aNameBase, aLocale ) )
1499 LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale), false );
1500 m_aLocaleItemVector.emplace_back( pLocaleItem );
1502 if( m_pCurrentLocaleItem == nullptr )
1503 m_pCurrentLocaleItem = pLocaleItem;
1505 if( m_pDefaultLocaleItem == nullptr )
1507 m_pDefaultLocaleItem = pLocaleItem;
1508 m_bDefaultModified = true;
1512 else if( !bDefaultFound && aExtension == "default" )
1514 if( checkNamingSceme( aPureName, m_aNameBase, aDefaultLocale ) )
1515 bDefaultFound = true;
1518 if( bDefaultFound )
1520 LocaleItem* pLocaleItem = getItemForLocale( aDefaultLocale, false );
1521 if( pLocaleItem )
1523 m_pDefaultLocaleItem = pLocaleItem;
1524 m_bDefaultModified = false;
1529 // Scan locale properties files
1530 void StringResourcePersistenceImpl::implScanLocales(std::unique_lock<std::mutex>& /*rGuard*/)
1532 // Dummy implementation, method not called for this
1533 // base class, but pure virtual not possible-
1536 bool StringResourcePersistenceImpl::loadLocale(std::unique_lock<std::mutex>& rGuard, LocaleItem* pLocaleItem)
1538 bool bSuccess = false;
1540 OSL_ENSURE( pLocaleItem, "StringResourcePersistenceImpl::loadLocale(): pLocaleItem == NULL" );
1541 if( pLocaleItem )
1543 if( pLocaleItem->m_bLoaded )
1545 bSuccess = true;
1547 else
1549 bSuccess = implLoadLocale(rGuard, pLocaleItem);
1550 pLocaleItem->m_bLoaded = true; // = bSuccess??? -> leads to more tries
1553 return bSuccess;
1556 bool StringResourcePersistenceImpl::implLoadLocale(std::unique_lock<std::mutex>& /*rGuard*/, LocaleItem*)
1558 // Dummy implementation, method not called for this
1559 // base class, but pure virtual not possible-
1560 return false;
1563 static OUString implGetNameScemeForLocaleItem( const LocaleItem* pLocaleItem )
1565 /* FIXME-BCP47: this uses '_' underscore character as separator and
1566 * also appends Variant, which can't be blindly changed as it would
1567 * violate the naming scheme in use. */
1569 static const char aUnder[] = "_";
1571 assert(pLocaleItem &&
1572 "StringResourcePersistenceImpl::implGetNameScemeForLocaleItem(): pLocaleItem == NULL");
1573 Locale aLocale = pLocaleItem->m_locale;
1575 OUString aRetStr = aUnder + aLocale.Language;
1577 OUString aCountry = aLocale.Country;
1578 if( !aCountry.isEmpty() )
1580 aRetStr += aUnder + aCountry;
1583 OUString aVariant = aLocale.Variant;
1584 if( !aVariant.isEmpty() )
1586 aRetStr += aUnder + aVariant;
1588 return aRetStr;
1591 OUString StringResourcePersistenceImpl::implGetFileNameForLocaleItem
1592 ( LocaleItem const * pLocaleItem, const OUString& aNameBase )
1594 OUString aFileName = aNameBase;
1595 if( aFileName.isEmpty() )
1596 aFileName = aNameBaseDefaultStr;
1598 aFileName += implGetNameScemeForLocaleItem( pLocaleItem );
1599 return aFileName;
1602 OUString StringResourcePersistenceImpl::implGetPathForLocaleItem
1603 ( LocaleItem const * pLocaleItem, const OUString& aNameBase,
1604 std::u16string_view aLocation, bool bDefaultFile )
1606 OUString aFileName = implGetFileNameForLocaleItem( pLocaleItem, aNameBase );
1607 INetURLObject aInetObj( aLocation );
1608 aInetObj.insertName( aFileName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All );
1609 if( bDefaultFile )
1610 aInetObj.setExtension( u"default" );
1611 else
1612 aInetObj.setExtension( u"properties" );
1613 OUString aCompleteFileName = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1614 return aCompleteFileName;
1617 // White space according to Java property files specification in
1618 // http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#load(java.io.InputStream)
1619 static bool isWhiteSpace( sal_Unicode c )
1621 bool bWhite = ( c == 0x0020 || // space
1622 c == 0x0009 || // tab
1623 c == 0x000a || // line feed, not always handled by TextInputStream
1624 c == 0x000d || // carriage return, not always handled by TextInputStream
1625 c == 0x000C ); // form feed
1626 return bWhite;
1629 static void skipWhites( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1631 while( ri < nLen )
1633 if( !isWhiteSpace( pBuf[ri] ) )
1634 break;
1635 ri++;
1639 static bool isHexDigit( sal_Unicode c, sal_uInt16& nDigitVal )
1641 bool bRet = true;
1642 if( c >= '0' && c <= '9' )
1643 nDigitVal = c - '0';
1644 else if( c >= 'a' && c <= 'f' )
1645 nDigitVal = c - 'a' + 10;
1646 else if( c >= 'A' && c <= 'F' )
1647 nDigitVal = c - 'A' + 10;
1648 else
1649 bRet = false;
1650 return bRet;
1653 static sal_Unicode getEscapeChar( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1655 sal_Int32 i = ri;
1657 sal_Unicode cRet = 0;
1658 sal_Unicode c = pBuf[i];
1659 switch( c )
1661 case 't':
1662 cRet = 0x0009;
1663 break;
1664 case 'n':
1665 cRet = 0x000a;
1666 break;
1667 case 'f':
1668 cRet = 0x000c;
1669 break;
1670 case 'r':
1671 cRet = 0x000d;
1672 break;
1673 case '\\':
1674 cRet = '\\';
1675 break;
1676 case 'u':
1678 // Skip multiple u
1679 i++;
1680 while( i < nLen && pBuf[i] == 'u' )
1681 i++;
1683 // Process hex digits
1684 sal_Int32 nDigitCount = 0;
1685 sal_uInt16 nDigitVal;
1686 while( i < nLen && isHexDigit( pBuf[i], nDigitVal ) )
1688 cRet = 16 * cRet + nDigitVal;
1690 nDigitCount++;
1691 if( nDigitCount == 4 )
1693 // Write back position
1694 ri = i;
1695 break;
1697 i++;
1699 break;
1701 default:
1702 cRet = c;
1705 return cRet;
1708 static void CheckContinueInNextLine( const Reference< io::XTextInputStream2 >& xTextInputStream,
1709 OUString& aLine, bool& bEscapePending, const sal_Unicode*& pBuf,
1710 sal_Int32& nLen, sal_Int32& i )
1712 if( !(i == nLen && bEscapePending) )
1713 return;
1715 bEscapePending = false;
1717 if( !xTextInputStream->isEOF() )
1719 aLine = xTextInputStream->readLine();
1720 nLen = aLine.getLength();
1721 pBuf = aLine.getStr();
1722 i = 0;
1724 skipWhites( pBuf, nLen, i );
1728 bool StringResourcePersistenceImpl::implReadPropertiesFile(
1729 std::unique_lock<std::mutex>& rGuard, LocaleItem* pLocaleItem,
1730 const Reference<io::XInputStream>& xInputStream)
1732 if( !xInputStream.is() || pLocaleItem == nullptr )
1733 return false;
1735 Reference< io::XTextInputStream2 > xTextInputStream = io::TextInputStream::create( m_xContext );
1737 xTextInputStream->setInputStream( xInputStream );
1739 OUString aEncodingStr = OUString::createFromAscii
1740 ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1741 xTextInputStream->setEncoding( aEncodingStr );
1743 OUString aLine;
1744 while( !xTextInputStream->isEOF() )
1746 aLine = xTextInputStream->readLine();
1748 sal_Int32 nLen = aLine.getLength();
1749 if( 0 == nLen )
1750 continue;
1751 const sal_Unicode* pBuf = aLine.getStr();
1752 OUStringBuffer aBuf;
1753 sal_Unicode c = 0;
1754 sal_Int32 i = 0;
1756 skipWhites( pBuf, nLen, i );
1757 if( i == nLen )
1758 continue; // line contains only white spaces
1760 // Comment?
1761 c = pBuf[i];
1762 if( c == '#' || c == '!' )
1763 continue;
1765 // Scan key
1766 OUString aResourceID;
1767 bool bEscapePending = false;
1768 bool bStrComplete = false;
1769 while( i < nLen && !bStrComplete )
1771 c = pBuf[i];
1772 if( bEscapePending )
1774 aBuf.append( getEscapeChar( pBuf, nLen, i ) );
1775 bEscapePending = false;
1777 else
1779 if( c == '\\' )
1781 bEscapePending = true;
1783 else
1785 if( c == ':' || c == '=' || isWhiteSpace( c ) )
1786 bStrComplete = true;
1787 else
1788 aBuf.append( c );
1791 i++;
1793 CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
1794 if( i == nLen )
1795 bStrComplete = true;
1797 if( bStrComplete )
1798 aResourceID = aBuf.makeStringAndClear();
1801 // Ignore lines with empty keys
1802 if( aResourceID.isEmpty() )
1803 continue;
1805 // Scan value
1806 skipWhites( pBuf, nLen, i );
1808 OUString aValueStr;
1809 bEscapePending = false;
1810 bStrComplete = false;
1811 while( i < nLen && !bStrComplete )
1813 c = pBuf[i];
1814 if( c == 0x000a || c == 0x000d ) // line feed/carriage return, not always handled by TextInputStream
1816 i++;
1818 else
1820 if( bEscapePending )
1822 aBuf.append( getEscapeChar( pBuf, nLen, i ) );
1823 bEscapePending = false;
1825 else if( c == '\\' )
1826 bEscapePending = true;
1827 else
1828 aBuf.append( c );
1829 i++;
1831 CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
1833 if( i == nLen )
1834 bStrComplete = true;
1836 if( bStrComplete )
1837 aValueStr = aBuf.makeStringAndClear();
1840 // Push into table
1841 pLocaleItem->m_aIdToStringMap[ aResourceID ] = aValueStr;
1842 implScanIdForNumber(rGuard, aResourceID);
1843 IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1844 rIndexMap[ aResourceID ] = pLocaleItem->m_nNextIndex++;
1847 return true;
1850 static sal_Unicode getHexCharForDigit( sal_uInt16 nDigitVal )
1852 if (nDigitVal < 10)
1853 return static_cast<sal_Unicode>('0' + nDigitVal);
1854 else
1855 return static_cast<sal_Unicode>('a' + (nDigitVal-10));
1858 static void implWriteCharToBuffer( OUStringBuffer& aBuf, sal_Unicode cu, bool bKey )
1860 if( cu == '\\' )
1862 aBuf.append( '\\' );
1863 aBuf.append( '\\' );
1865 else if( cu == 0x000a )
1867 aBuf.append( '\\' );
1868 aBuf.append( 'n' );
1870 else if( cu == 0x000d )
1872 aBuf.append( '\\' );
1873 aBuf.append( 'r' );
1875 else if( bKey && cu == '=' )
1877 aBuf.append( '\\' );
1878 aBuf.append( '=' );
1880 else if( bKey && cu == ':' )
1882 aBuf.append( '\\' );
1883 aBuf.append( ':' );
1885 // ISO/IEC 8859-1 range according to:
1886 // http://en.wikipedia.org/wiki/ISO/IEC_8859-1
1887 else if( cu >= 0x20 && cu <= 0x7e )
1888 //TODO: Check why (cu >= 0xa0 && cu <= 0xFF)
1889 //is encoded in sample properties files
1890 //else if( (cu >= 0x20 && cu <= 0x7e) ||
1891 // (cu >= 0xa0 && cu <= 0xFF) )
1893 aBuf.append( cu );
1895 else
1897 // Unicode encoding
1898 aBuf.append( '\\' );
1899 aBuf.append( 'u' );
1901 sal_uInt16 nVal = cu;
1902 for( sal_uInt16 i = 0 ; i < 4 ; i++ )
1904 sal_uInt16 nDigit = nVal / 0x1000;
1905 nVal -= nDigit * 0x1000;
1906 nVal *= 0x10;
1907 aBuf.append( getHexCharForDigit( nDigit ) );
1912 static void implWriteStringWithEncoding( const OUString& aStr,
1913 Reference< io::XTextOutputStream2 > const & xTextOutputStream, bool bKey )
1915 static const sal_Unicode cLineFeed = 0xa;
1917 OUStringBuffer aBuf;
1918 sal_Int32 nLen = aStr.getLength();
1919 const sal_Unicode* pSrc = aStr.getStr();
1920 for( sal_Int32 i = 0 ; i < nLen ; i++ )
1922 sal_Unicode cu = pSrc[i];
1923 implWriteCharToBuffer( aBuf, cu, bKey );
1924 // TODO?: split long lines
1926 if( !bKey )
1927 aBuf.append( cLineFeed );
1929 OUString aWriteStr = aBuf.makeStringAndClear();
1930 xTextOutputStream->writeString( aWriteStr );
1933 bool StringResourcePersistenceImpl::implWritePropertiesFile( LocaleItem const * pLocaleItem,
1934 const Reference< io::XOutputStream >& xOutputStream, const OUString& aComment )
1936 if( !xOutputStream.is() || pLocaleItem == nullptr )
1937 return false;
1939 bool bSuccess = false;
1940 Reference< io::XTextOutputStream2 > xTextOutputStream = io::TextOutputStream::create(m_xContext);
1942 xTextOutputStream->setOutputStream( xOutputStream );
1944 OUString aEncodingStr = OUString::createFromAscii
1945 ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1946 xTextOutputStream->setEncoding( aEncodingStr );
1948 xTextOutputStream->writeString( aComment );
1949 xTextOutputStream->writeString( u"\n"_ustr );
1951 const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
1952 if( !rHashMap.empty() )
1954 // Sort ids according to read order
1955 const IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1957 // Find max/min index
1958 auto itMinMax = std::minmax_element(rIndexMap.begin(), rIndexMap.end(),
1959 [](const IdToIndexMap::value_type& a, const IdToIndexMap::value_type& b) { return a.second < b.second; });
1960 sal_Int32 nMinIndex = itMinMax.first->second;
1961 sal_Int32 nMaxIndex = itMinMax.second->second;
1962 sal_Int32 nTabSize = nMaxIndex - nMinIndex + 1;
1964 // Create sorted array of pointers to the id strings
1965 std::unique_ptr<const OUString*[]> pIdPtrs( new const OUString*[nTabSize] );
1966 for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
1967 pIdPtrs[i] = nullptr;
1968 for( const auto& rIndex : rIndexMap )
1970 sal_Int32 nIndex = rIndex.second;
1971 pIdPtrs[nIndex - nMinIndex] = &(rIndex.first);
1974 // Write lines in correct order
1975 for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
1977 const OUString* pStr = pIdPtrs[i];
1978 if( pStr != nullptr )
1980 OUString aResourceID = *pStr;
1981 IdToStringMap::const_iterator it = rHashMap.find( aResourceID );
1982 if( it != rHashMap.end() )
1984 implWriteStringWithEncoding( aResourceID, xTextOutputStream, true );
1985 xTextOutputStream->writeString( u"="_ustr );
1986 OUString aValStr = (*it).second;
1987 implWriteStringWithEncoding( aValStr, xTextOutputStream, false );
1993 bSuccess = true;
1995 return bSuccess;
1999 // StringResourceWithStorageImpl
2001 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2002 scripting_StringResourceWithStorageImpl_get_implementation(
2003 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2005 return cppu::acquire(new StringResourceWithStorageImpl(context));
2009 StringResourceWithStorageImpl::StringResourceWithStorageImpl( const Reference< XComponentContext >& rxContext )
2010 : StringResourceWithStorageImpl_BASE( rxContext )
2011 , m_bStorageChanged( false )
2016 StringResourceWithStorageImpl::~StringResourceWithStorageImpl()
2021 // XServiceInfo
2024 OUString StringResourceWithStorageImpl::getImplementationName( )
2026 return u"com.sun.star.comp.scripting.StringResourceWithStorage"_ustr;
2029 sal_Bool StringResourceWithStorageImpl::supportsService( const OUString& rServiceName )
2031 return cppu::supportsService(this, rServiceName);
2034 Sequence< OUString > StringResourceWithStorageImpl::getSupportedServiceNames( )
2036 return { u"com.sun.star.resource.StringResourceWithStorage"_ustr };
2040 // XInitialization
2043 void StringResourceWithStorageImpl::initialize( const Sequence< Any >& aArguments )
2045 std::unique_lock aGuard( m_aMutex );
2047 if ( aArguments.getLength() != 5 )
2049 throw RuntimeException(
2050 u"StringResourceWithStorageImpl::initialize: invalid number of arguments!"_ustr );
2053 bool bOk = (aArguments[0] >>= m_xStorage);
2054 if( bOk && !m_xStorage.is() )
2055 bOk = false;
2057 if( !bOk )
2059 throw IllegalArgumentException( u"StringResourceWithStorageImpl::initialize: invalid storage"_ustr, Reference< XInterface >(), 0 );
2062 implInitializeCommonParameters( aGuard, aArguments );
2066 // Forwarding calls to base class
2068 // XModifyBroadcaster
2069 void StringResourceWithStorageImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2071 StringResourceImpl::addModifyListener( aListener );
2073 void StringResourceWithStorageImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2075 StringResourceImpl::removeModifyListener( aListener );
2078 // XStringResourceResolver
2079 OUString StringResourceWithStorageImpl::resolveString( const OUString& ResourceID )
2081 return StringResourceImpl::resolveString( ResourceID ) ;
2083 OUString StringResourceWithStorageImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2085 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2087 sal_Bool StringResourceWithStorageImpl::hasEntryForId( const OUString& ResourceID )
2089 return StringResourceImpl::hasEntryForId( ResourceID ) ;
2091 sal_Bool StringResourceWithStorageImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2092 const Locale& locale )
2094 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2096 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDs( )
2098 return StringResourceImpl::getResourceIDs();
2100 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDsForLocale
2101 ( const Locale& locale )
2103 return StringResourceImpl::getResourceIDsForLocale( locale );
2105 Locale StringResourceWithStorageImpl::getCurrentLocale()
2107 return StringResourceImpl::getCurrentLocale();
2109 Locale StringResourceWithStorageImpl::getDefaultLocale( )
2111 return StringResourceImpl::getDefaultLocale();
2113 Sequence< Locale > StringResourceWithStorageImpl::getLocales( )
2115 return StringResourceImpl::getLocales();
2118 // XStringResourceManager
2119 sal_Bool StringResourceWithStorageImpl::isReadOnly()
2121 return StringResourceImpl::isReadOnly();
2123 void StringResourceWithStorageImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2125 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2127 void StringResourceWithStorageImpl::setDefaultLocale( const Locale& locale )
2129 StringResourceImpl::setDefaultLocale( locale );
2131 void StringResourceWithStorageImpl::setString( const OUString& ResourceID, const OUString& Str )
2133 StringResourceImpl::setString( ResourceID, Str );
2135 void StringResourceWithStorageImpl::setStringForLocale
2136 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2138 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2140 void StringResourceWithStorageImpl::removeId( const OUString& ResourceID )
2142 StringResourceImpl::removeId( ResourceID );
2144 void StringResourceWithStorageImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2146 StringResourceImpl::removeIdForLocale( ResourceID, locale );
2148 void StringResourceWithStorageImpl::newLocale( const Locale& locale )
2150 StringResourceImpl::newLocale( locale );
2152 void StringResourceWithStorageImpl::removeLocale( const Locale& locale )
2154 StringResourceImpl::removeLocale( locale );
2156 sal_Int32 StringResourceWithStorageImpl::getUniqueNumericId( )
2158 return StringResourceImpl::getUniqueNumericId();
2161 // XStringResourcePersistence
2162 void StringResourceWithStorageImpl::store()
2164 std::unique_lock aGuard( m_aMutex );
2165 implCheckReadOnly( "StringResourceWithStorageImpl::store(): Read only" );
2167 bool bStoreAll = m_bStorageChanged;
2168 m_bStorageChanged = false;
2169 if( !m_bModified && !bStoreAll )
2170 return;
2172 implStoreAtStorage(aGuard, m_aNameBase, m_aComment, m_xStorage, true/*bUsedForStore*/, bStoreAll);
2173 m_bModified = false;
2176 sal_Bool StringResourceWithStorageImpl::isModified( )
2178 return StringResourcePersistenceImpl::isModified();
2180 void StringResourceWithStorageImpl::setComment( const OUString& Comment )
2182 StringResourcePersistenceImpl::setComment( Comment );
2184 void StringResourceWithStorageImpl::storeToStorage( const Reference< XStorage >& Storage,
2185 const OUString& NameBase, const OUString& Comment )
2187 StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2189 void StringResourceWithStorageImpl::storeToURL( const OUString& URL,
2190 const OUString& NameBase, const OUString& Comment,
2191 const Reference< css::task::XInteractionHandler >& Handler )
2193 StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2195 Sequence< ::sal_Int8 > StringResourceWithStorageImpl::exportBinary( )
2197 return StringResourcePersistenceImpl::exportBinary();
2199 void StringResourceWithStorageImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2201 StringResourcePersistenceImpl::importBinary( Data );
2205 // XStringResourceWithStorage
2207 void StringResourceWithStorageImpl::storeAsStorage( const Reference< XStorage >& Storage )
2209 setStorage( Storage );
2210 store();
2213 void StringResourceWithStorageImpl::setStorage( const Reference< XStorage >& Storage )
2215 std::unique_lock aGuard( m_aMutex );
2217 if( !Storage.is() )
2219 throw IllegalArgumentException( u"StringResourceWithStorageImpl::setStorage: invalid storage"_ustr, Reference< XInterface >(), 0 );
2222 implLoadAllLocales(aGuard);
2224 m_xStorage = Storage;
2225 m_bStorageChanged = true;
2229 // Private helper methods
2232 // Scan locale properties files
2233 void StringResourceWithStorageImpl::implScanLocales(std::unique_lock<std::mutex>& rGuard)
2235 if( m_xStorage.is() )
2237 Sequence< OUString > aContentSeq = m_xStorage->getElementNames();
2238 implScanLocaleNames( aContentSeq );
2241 implLoadAllLocales(rGuard);
2244 // Loading
2245 bool StringResourceWithStorageImpl::implLoadLocale(std::unique_lock<std::mutex>& rGuard, LocaleItem* pLocaleItem)
2247 bool bSuccess = false;
2250 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem, m_aNameBase ) + ".properties";
2252 Reference< io::XStream > xElementStream =
2253 m_xStorage->openStreamElement( aStreamName, ElementModes::READ );
2255 if( xElementStream.is() )
2257 Reference< io::XInputStream > xInputStream = xElementStream->getInputStream();
2258 if( xInputStream.is() )
2260 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile(rGuard, pLocaleItem, xInputStream);
2261 xInputStream->closeInput();
2265 catch( uno::Exception& )
2268 return bSuccess;
2272 // StringResourceWithLocationImpl
2275 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2276 scripting_StringResourceWithLocationImpl_get_implementation(
2277 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2279 return cppu::acquire(new StringResourceWithLocationImpl(context));
2284 StringResourceWithLocationImpl::StringResourceWithLocationImpl( const Reference< XComponentContext >& rxContext )
2285 : StringResourceWithLocationImpl_BASE( rxContext )
2286 , m_bLocationChanged( false )
2291 StringResourceWithLocationImpl::~StringResourceWithLocationImpl()
2296 // XServiceInfo
2299 OUString StringResourceWithLocationImpl::getImplementationName( )
2301 return u"com.sun.star.comp.scripting.StringResourceWithLocation"_ustr;
2304 sal_Bool StringResourceWithLocationImpl::supportsService( const OUString& rServiceName )
2306 return cppu::supportsService(this, rServiceName);
2309 Sequence< OUString > StringResourceWithLocationImpl::getSupportedServiceNames( )
2311 return { u"com.sun.star.resource.StringResourceWithLocation"_ustr };
2315 // XInitialization
2318 void StringResourceWithLocationImpl::initialize( const Sequence< Any >& aArguments )
2320 std::unique_lock aGuard( m_aMutex );
2322 if ( aArguments.getLength() != 6 )
2324 throw RuntimeException(
2325 u"XInitialization::initialize: invalid number of arguments!"_ustr );
2328 bool bOk = (aArguments[0] >>= m_aLocation);
2329 sal_Int32 nLen = m_aLocation.getLength();
2330 if( bOk && nLen == 0 )
2332 bOk = false;
2334 else
2336 if( m_aLocation[nLen - 1] != '/' )
2337 m_aLocation += "/";
2340 if( !bOk )
2342 throw IllegalArgumentException( u"XInitialization::initialize: invalid URL"_ustr, Reference< XInterface >(), 0 );
2346 bOk = (aArguments[5] >>= m_xInteractionHandler);
2347 if( !bOk )
2349 throw IllegalArgumentException( u"StringResourceWithStorageImpl::initialize: invalid type"_ustr, Reference< XInterface >(), 5 );
2352 implInitializeCommonParameters( aGuard, aArguments );
2356 // Forwarding calls to base class
2358 // XModifyBroadcaster
2359 void StringResourceWithLocationImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2361 StringResourceImpl::addModifyListener( aListener );
2363 void StringResourceWithLocationImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2365 StringResourceImpl::removeModifyListener( aListener );
2368 // XStringResourceResolver
2369 OUString StringResourceWithLocationImpl::resolveString( const OUString& ResourceID )
2371 return StringResourceImpl::resolveString( ResourceID ) ;
2373 OUString StringResourceWithLocationImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2375 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2377 sal_Bool StringResourceWithLocationImpl::hasEntryForId( const OUString& ResourceID )
2379 return StringResourceImpl::hasEntryForId( ResourceID ) ;
2381 sal_Bool StringResourceWithLocationImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2382 const Locale& locale )
2384 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2386 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDs( )
2388 return StringResourceImpl::getResourceIDs();
2390 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDsForLocale
2391 ( const Locale& locale )
2393 return StringResourceImpl::getResourceIDsForLocale( locale );
2395 Locale StringResourceWithLocationImpl::getCurrentLocale()
2397 return StringResourceImpl::getCurrentLocale();
2399 Locale StringResourceWithLocationImpl::getDefaultLocale( )
2401 return StringResourceImpl::getDefaultLocale();
2403 Sequence< Locale > StringResourceWithLocationImpl::getLocales( )
2405 return StringResourceImpl::getLocales();
2408 // XStringResourceManager
2409 sal_Bool StringResourceWithLocationImpl::isReadOnly()
2411 return StringResourceImpl::isReadOnly();
2413 void StringResourceWithLocationImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2415 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2417 void StringResourceWithLocationImpl::setDefaultLocale( const Locale& locale )
2419 StringResourceImpl::setDefaultLocale( locale );
2421 void StringResourceWithLocationImpl::setString( const OUString& ResourceID, const OUString& Str )
2423 StringResourceImpl::setString( ResourceID, Str );
2425 void StringResourceWithLocationImpl::setStringForLocale
2426 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2428 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2430 void StringResourceWithLocationImpl::removeId( const OUString& ResourceID )
2432 StringResourceImpl::removeId( ResourceID );
2434 void StringResourceWithLocationImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2436 StringResourceImpl::removeIdForLocale( ResourceID, locale );
2438 void StringResourceWithLocationImpl::newLocale( const Locale& locale )
2440 StringResourceImpl::newLocale( locale );
2442 void StringResourceWithLocationImpl::removeLocale( const Locale& locale )
2444 StringResourceImpl::removeLocale( locale );
2446 sal_Int32 StringResourceWithLocationImpl::getUniqueNumericId( )
2448 return StringResourceImpl::getUniqueNumericId();
2451 // XStringResourcePersistence
2452 void StringResourceWithLocationImpl::store()
2454 std::unique_lock aGuard( m_aMutex );
2455 implCheckReadOnly( "StringResourceWithLocationImpl::store(): Read only" );
2457 bool bStoreAll = m_bLocationChanged;
2458 m_bLocationChanged = false;
2459 if( !m_bModified && !bStoreAll )
2460 return;
2462 Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2463 implStoreAtLocation(aGuard, m_aLocation, m_aNameBase, m_aComment,
2464 xFileAccess, true/*bUsedForStore*/, bStoreAll );
2465 m_bModified = false;
2468 sal_Bool StringResourceWithLocationImpl::isModified( )
2470 return StringResourcePersistenceImpl::isModified();
2472 void StringResourceWithLocationImpl::setComment( const OUString& Comment )
2474 StringResourcePersistenceImpl::setComment( Comment );
2476 void StringResourceWithLocationImpl::storeToStorage( const Reference< XStorage >& Storage,
2477 const OUString& NameBase, const OUString& Comment )
2479 StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2481 void StringResourceWithLocationImpl::storeToURL( const OUString& URL,
2482 const OUString& NameBase, const OUString& Comment,
2483 const Reference< css::task::XInteractionHandler >& Handler )
2485 StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2487 Sequence< ::sal_Int8 > StringResourceWithLocationImpl::exportBinary( )
2489 return StringResourcePersistenceImpl::exportBinary();
2491 void StringResourceWithLocationImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2493 StringResourcePersistenceImpl::importBinary( Data );
2497 // XStringResourceWithLocation
2499 // XStringResourceWithLocation
2500 void StringResourceWithLocationImpl::storeAsURL( const OUString& URL )
2502 setURL( URL );
2503 store();
2506 void StringResourceWithLocationImpl::setURL( const OUString& URL )
2508 std::unique_lock aGuard( m_aMutex );
2509 implCheckReadOnly( "StringResourceWithLocationImpl::setURL(): Read only" );
2511 sal_Int32 nLen = URL.getLength();
2512 if( nLen == 0 )
2514 throw IllegalArgumentException( u"StringResourceWithLocationImpl::setURL: invalid URL"_ustr, Reference< XInterface >(), 0 );
2517 implLoadAllLocales(aGuard);
2519 // Delete files at old location
2520 implStoreAtLocation(aGuard, m_aLocation, m_aNameBase, m_aComment,
2521 getFileAccessImpl(), false/*bUsedForStore*/, false/*bStoreAll*/, true/*bKillAll*/ );
2523 m_aLocation = URL;
2524 m_bLocationChanged = true;
2528 // Private helper methods
2531 // Scan locale properties files
2532 void StringResourceWithLocationImpl::implScanLocales(std::unique_lock<std::mutex>& /*rGuard*/)
2534 const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2535 if( xFileAccess->isFolder( m_aLocation ) )
2537 Sequence< OUString > aContentSeq = xFileAccess->getFolderContents( m_aLocation, false );
2538 implScanLocaleNames( aContentSeq );
2542 // Loading
2543 bool StringResourceWithLocationImpl::implLoadLocale(std::unique_lock<std::mutex>& rGuard, LocaleItem* pLocaleItem)
2545 bool bSuccess = false;
2547 const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2548 OUString aCompleteFileName =
2549 implGetPathForLocaleItem( pLocaleItem, m_aNameBase, m_aLocation );
2551 Reference< io::XInputStream > xInputStream;
2554 xInputStream = xFileAccess->openFileRead( aCompleteFileName );
2556 catch( Exception& )
2558 if( xInputStream.is() )
2560 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile(rGuard, pLocaleItem, xInputStream);
2561 xInputStream->closeInput();
2564 return bSuccess;
2567 const Reference< ucb::XSimpleFileAccess3 > & StringResourceWithLocationImpl::getFileAccessImpl()
2569 if( !m_xSFI.is() )
2571 m_xSFI = ucb::SimpleFileAccess::create(m_xContext);
2573 if( m_xInteractionHandler.is() )
2574 m_xSFI->setInteractionHandler( m_xInteractionHandler );
2576 return m_xSFI;
2579 } // namespace stringresource
2582 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */