Avoid potential negative array index access to cached text.
[LibreOffice.git] / scripting / source / stringresource / stringresource.cxx
blob79090c6eeaad01924e22f2763305a8004d3dd23a
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 "com.sun.star.comp.scripting.StringResource";
91 sal_Bool StringResourceImpl::supportsService( const OUString& rServiceName )
93 return cppu::supportsService(this, rServiceName);
96 Sequence< OUString > StringResourceImpl::getSupportedServiceNames( )
98 return { "com.sun.star.resource.StringResource" };
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 ( const OUString& ResourceID, LocaleItem* pLocaleItem )
128 OUString aRetStr;
129 bool bSuccess = false;
130 if( pLocaleItem != nullptr && loadLocale( 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( 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( ResourceID, pLocaleItem );
159 bool StringResourceImpl::implHasEntryForId( const OUString& ResourceID, LocaleItem* pLocaleItem )
161 bool bSuccess = false;
162 if( pLocaleItem != nullptr && loadLocale( 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( 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( ResourceID, pLocaleItem );
185 Sequence< OUString > StringResourceImpl::implGetResourceIDs( LocaleItem* pLocaleItem )
187 Sequence< OUString > aIDSeq( 0 );
188 if( pLocaleItem && loadLocale( 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( pLocaleItem );
213 Sequence< OUString > StringResourceImpl::getResourceIDs( )
215 std::unique_lock aGuard( m_aMutex );
216 return implGetResourceIDs( 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( 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( 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( 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( 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( "StringResourceImpl: locale already exists" );
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( 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 OUString aStr = rEntry.second;
421 rTargetMap[ aId ] = aStr;
424 const IdToIndexMap& rSourceIndexMap = pCopyFromItem->m_aIdToIndexMap;
425 IdToIndexMap& rTargetIndexMap = pLocaleItem->m_aIdToIndexMap;
426 for( const auto& rIndex : rSourceIndexMap )
428 OUString aId = rIndex.first;
429 sal_Int32 nIndex = rIndex.second;
430 rTargetIndexMap[ aId ] = nIndex;
432 pLocaleItem->m_nNextIndex = pCopyFromItem->m_nNextIndex;
435 if( m_pCurrentLocaleItem == nullptr )
436 m_pCurrentLocaleItem = pLocaleItem;
438 if( m_pDefaultLocaleItem == nullptr )
440 m_pDefaultLocaleItem = pLocaleItem;
441 m_bDefaultModified = true;
444 implModified(aGuard);
447 void StringResourceImpl::removeLocale( const Locale& locale )
449 std::unique_lock aGuard( m_aMutex );
450 implCheckReadOnly( "StringResourceImpl::removeLocale(): Read only" );
452 LocaleItem* pRemoveItem = getItemForLocale( locale, true );
453 if( !pRemoveItem )
454 return;
456 // Last locale?
457 sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
458 if( nLocaleCount > 1 )
460 if( m_pCurrentLocaleItem == pRemoveItem ||
461 m_pDefaultLocaleItem == pRemoveItem )
463 LocaleItem* pFallbackItem = nullptr;
464 for( const auto& pLocaleItem : m_aLocaleItemVector )
466 if( pLocaleItem.get() != pRemoveItem )
468 pFallbackItem = pLocaleItem.get();
469 break;
472 if( m_pCurrentLocaleItem == pRemoveItem )
474 setCurrentLocale( pFallbackItem->m_locale, false/*FindClosestMatch*/ );
476 if( m_pDefaultLocaleItem == pRemoveItem )
478 setDefaultLocale( pFallbackItem->m_locale );
482 auto it = std::find_if(m_aLocaleItemVector.begin(), m_aLocaleItemVector.end(),
483 [&pRemoveItem](const std::unique_ptr<LocaleItem>& rxItem) { return rxItem.get() == pRemoveItem; });
484 if (it == m_aLocaleItemVector.end())
485 return;
487 // Remember locale item to delete file while storing
488 m_aDeletedLocaleItemVector.push_back( std::move(*it) );
490 // Last locale?
491 if( nLocaleCount == 1 )
493 m_nNextUniqueNumericId = 0;
494 if( m_pDefaultLocaleItem )
496 m_aChangedDefaultLocaleVector.push_back(
497 std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
499 m_pCurrentLocaleItem = nullptr;
500 m_pDefaultLocaleItem = nullptr;
503 m_aLocaleItemVector.erase( it );
505 implModified(aGuard);
508 void StringResourceImpl::implScanIdForNumber( const OUString& ResourceID )
510 const sal_Unicode* pSrc = ResourceID.getStr();
511 sal_Int32 nLen = ResourceID.getLength();
513 sal_Int32 nNumber = 0;
514 for( sal_Int32 i = 0 ; i < nLen ; i++ )
516 sal_Unicode c = pSrc[i];
517 if( c >= '0' && c <= '9' )
519 sal_uInt16 nDigitVal = c - '0';
520 nNumber = 10*nNumber + nDigitVal;
522 else
523 break;
526 if( m_nNextUniqueNumericId < nNumber + 1 )
527 m_nNextUniqueNumericId = nNumber + 1;
530 sal_Int32 StringResourceImpl::getUniqueNumericId( )
532 if( m_nNextUniqueNumericId == UNIQUE_NUMBER_NEEDS_INITIALISATION )
534 implLoadAllLocales();
535 m_nNextUniqueNumericId = 0;
538 if( m_nNextUniqueNumericId < UNIQUE_NUMBER_NEEDS_INITIALISATION )
540 throw NoSupportException( "getUniqueNumericId: Extended sal_Int32 range" );
542 return m_nNextUniqueNumericId;
546 // Private helper methods
548 LocaleItem* StringResourceImpl::getItemForLocale
549 ( const Locale& locale, bool bException )
551 LocaleItem* pRetItem = nullptr;
553 // Search for locale
554 for( auto& pLocaleItem : m_aLocaleItemVector )
556 if( pLocaleItem )
558 Locale& cmp_locale = pLocaleItem->m_locale;
559 if( cmp_locale.Language == locale.Language &&
560 cmp_locale.Country == locale.Country &&
561 cmp_locale.Variant == locale.Variant )
563 pRetItem = pLocaleItem.get();
564 break;
569 if( pRetItem == nullptr && bException )
571 throw IllegalArgumentException( "StringResourceImpl: Invalid locale", Reference< XInterface >(), 0 );
573 return pRetItem;
576 // Returns the LocaleItem for a given locale, if it exists, otherwise NULL.
577 // This method performs a closest match search, at least the language must match.
578 LocaleItem* StringResourceImpl::getClosestMatchItemForLocale( const Locale& locale )
580 LocaleItem* pRetItem = nullptr;
582 ::std::vector< Locale > aLocales( m_aLocaleItemVector.size());
583 size_t i = 0;
584 for( const auto& pLocaleItem : m_aLocaleItemVector )
586 aLocales[i] = (pLocaleItem ? pLocaleItem->m_locale : Locale());
587 ++i;
589 ::std::vector< Locale >::const_iterator iFound( LanguageTag::getMatchingFallback( aLocales, locale));
590 if (iFound != aLocales.end())
591 pRetItem = (m_aLocaleItemVector.begin() + (iFound - aLocales.begin()))->get();
593 return pRetItem;
596 void StringResourceImpl::implModified(std::unique_lock<std::mutex>& rGuard)
598 m_bModified = true;
599 implNotifyListeners(rGuard);
602 void StringResourceImpl::implNotifyListeners(std::unique_lock<std::mutex>& rGuard)
604 EventObject aEvent;
605 aEvent.Source = getXWeak();
606 m_aListenerContainer.forEach(rGuard,
607 [&aEvent](const css::uno::Reference<XModifyListener>& xListener)
609 xListener->modified(aEvent);
615 // Loading
617 bool StringResourceImpl::loadLocale( LocaleItem* )
619 // Base implementation has nothing to load
620 return true;
623 void StringResourceImpl::implLoadAllLocales()
625 // Base implementation has nothing to load
629 // StringResourcePersistenceImpl
632 StringResourcePersistenceImpl::StringResourcePersistenceImpl( const Reference< XComponentContext >& rxContext )
633 : StringResourcePersistenceImpl_BASE( rxContext )
638 StringResourcePersistenceImpl::~StringResourcePersistenceImpl()
643 // XServiceInfo
646 OUString StringResourcePersistenceImpl::getImplementationName( )
648 return "com.sun.star.comp.scripting.StringResource";
652 sal_Bool StringResourcePersistenceImpl::supportsService( const OUString& rServiceName )
654 return cppu::supportsService( this, rServiceName );
658 Sequence< OUString > StringResourcePersistenceImpl::getSupportedServiceNames( )
660 return StringResourceImpl::getSupportedServiceNames();
664 // XInitialization base functionality for derived classes
667 constexpr OUString aNameBaseDefaultStr = u"strings"_ustr;
669 void StringResourcePersistenceImpl::implInitializeCommonParameters
670 ( std::unique_lock<std::mutex>& rGuard, const Sequence< Any >& aArguments )
672 bool bReadOnlyOk = (aArguments[1] >>= m_bReadOnly);
673 if( !bReadOnlyOk )
675 throw IllegalArgumentException( "XInitialization::initialize: Expected ReadOnly flag", Reference< XInterface >(), 1 );
678 css::lang::Locale aCurrentLocale;
679 bool bLocaleOk = (aArguments[2] >>= aCurrentLocale);
680 if( !bLocaleOk )
682 throw IllegalArgumentException( "XInitialization::initialize: Expected Locale", Reference< XInterface >(), 2 );
685 bool bNameBaseOk = (aArguments[3] >>= m_aNameBase);
686 if( !bNameBaseOk )
688 throw IllegalArgumentException( "XInitialization::initialize: Expected NameBase string", Reference< XInterface >(), 3 );
690 if( m_aNameBase.isEmpty() )
691 m_aNameBase = aNameBaseDefaultStr;
693 bool bCommentOk = (aArguments[4] >>= m_aComment);
694 if( !bCommentOk )
696 throw IllegalArgumentException( "XInitialization::initialize: Expected Comment string", Reference< XInterface >(), 4 );
699 implScanLocales();
701 implSetCurrentLocale( rGuard, aCurrentLocale, true/*FindClosestMatch*/, true/*bUseDefaultIfNoMatch*/ );
705 // Forwarding calls to base class
707 // XModifyBroadcaster
708 void StringResourcePersistenceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
710 StringResourceImpl::addModifyListener( aListener );
712 void StringResourcePersistenceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
714 StringResourceImpl::removeModifyListener( aListener );
717 // XStringResourceResolver
718 OUString StringResourcePersistenceImpl::resolveString( const OUString& ResourceID )
720 return StringResourceImpl::resolveString( ResourceID ) ;
722 OUString StringResourcePersistenceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
724 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
726 sal_Bool StringResourcePersistenceImpl::hasEntryForId( const OUString& ResourceID )
728 return StringResourceImpl::hasEntryForId( ResourceID ) ;
730 sal_Bool StringResourcePersistenceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
731 const Locale& locale )
733 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
735 Locale StringResourcePersistenceImpl::getCurrentLocale()
737 return StringResourceImpl::getCurrentLocale();
739 Locale StringResourcePersistenceImpl::getDefaultLocale( )
741 return StringResourceImpl::getDefaultLocale();
743 Sequence< Locale > StringResourcePersistenceImpl::getLocales( )
745 return StringResourceImpl::getLocales();
748 // XStringResourceManager
749 sal_Bool StringResourcePersistenceImpl::isReadOnly()
751 return StringResourceImpl::isReadOnly();
753 void StringResourcePersistenceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
755 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
757 void StringResourcePersistenceImpl::setDefaultLocale( const Locale& locale )
759 StringResourceImpl::setDefaultLocale( locale );
761 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDs( )
763 return StringResourceImpl::getResourceIDs();
765 void StringResourcePersistenceImpl::setString( const OUString& ResourceID, const OUString& Str )
767 StringResourceImpl::setString( ResourceID, Str );
769 void StringResourcePersistenceImpl::setStringForLocale
770 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
772 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
774 Sequence< OUString > StringResourcePersistenceImpl::getResourceIDsForLocale
775 ( const Locale& locale )
777 return StringResourceImpl::getResourceIDsForLocale( locale );
779 void StringResourcePersistenceImpl::removeId( const OUString& ResourceID )
781 StringResourceImpl::removeId( ResourceID );
783 void StringResourcePersistenceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
785 StringResourceImpl::removeIdForLocale( ResourceID, locale );
787 void StringResourcePersistenceImpl::newLocale( const Locale& locale )
789 StringResourceImpl::newLocale( locale );
791 void StringResourcePersistenceImpl::removeLocale( const Locale& locale )
793 StringResourceImpl::removeLocale( locale );
795 sal_Int32 StringResourcePersistenceImpl::getUniqueNumericId( )
797 return StringResourceImpl::getUniqueNumericId();
801 // XStringResourcePersistence
803 void StringResourcePersistenceImpl::store()
807 sal_Bool StringResourcePersistenceImpl::isModified( )
809 std::unique_lock aGuard( m_aMutex );
811 return m_bModified;
814 void StringResourcePersistenceImpl::setComment( const OUString& Comment )
816 m_aComment = Comment;
819 void StringResourcePersistenceImpl::storeToStorage( const Reference< XStorage >& Storage,
820 const OUString& NameBase, const OUString& Comment )
822 std::unique_lock aGuard( m_aMutex );
824 implStoreAtStorage( NameBase, Comment, Storage, false/*bUsedForStore*/, true/*bStoreAll*/ );
827 void StringResourcePersistenceImpl::implStoreAtStorage
829 const OUString& aNameBase,
830 const OUString& aComment,
831 const Reference< css::embed::XStorage >& Storage,
832 bool bUsedForStore,
833 bool bStoreAll
836 // Delete files for deleted locales
837 if( bUsedForStore )
839 for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
841 if( pLocaleItem )
843 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".properties";
847 Storage->removeElement( aStreamName );
849 catch( Exception& )
852 pLocaleItem.reset();
855 m_aDeletedLocaleItemVector.clear();
858 for( auto& pLocaleItem : m_aLocaleItemVector )
860 if( pLocaleItem != nullptr && (bStoreAll || pLocaleItem->m_bModified) &&
861 loadLocale( pLocaleItem.get() ) )
863 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), aNameBase ) + ".properties";
865 Reference< io::XStream > xElementStream =
866 Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
868 uno::Reference< beans::XPropertySet > xProps( xElementStream, uno::UNO_QUERY );
869 OSL_ENSURE( xProps.is(), "The StorageStream must implement XPropertySet interface!" );
870 if ( xProps.is() )
872 OUString aPropName("MediaType");
873 xProps->setPropertyValue( aPropName, uno::Any( OUString("text/plain") ) );
875 aPropName = "UseCommonStoragePasswordEncryption";
876 xProps->setPropertyValue( aPropName, uno::Any( true ) );
879 Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
880 if( xOutputStream.is() )
881 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
882 xOutputStream->closeOutput();
884 if( bUsedForStore )
885 pLocaleItem->m_bModified = false;
889 // Delete files for changed defaults
890 if( bUsedForStore )
892 for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
894 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".default";
898 Storage->removeElement( aStreamName );
900 catch( Exception& )
903 pLocaleItem.reset();
905 m_aChangedDefaultLocaleVector.clear();
908 // Default locale
909 if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || m_bDefaultModified)) )
910 return;
912 OUString aStreamName = implGetFileNameForLocaleItem( m_pDefaultLocaleItem, aNameBase ) + ".default";
914 Reference< io::XStream > xElementStream =
915 Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
917 // Only create stream without content
918 Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
919 xOutputStream->closeOutput();
921 if( bUsedForStore )
922 m_bDefaultModified = false;
925 void StringResourcePersistenceImpl::storeToURL( const OUString& URL,
926 const OUString& NameBase, const OUString& Comment,
927 const Reference< css::task::XInteractionHandler >& Handler )
929 std::unique_lock aGuard( m_aMutex );
931 Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create(m_xContext);
932 if( xFileAccess.is() && Handler.is() )
933 xFileAccess->setInteractionHandler( Handler );
935 implStoreAtLocation( URL, NameBase, Comment, xFileAccess, false/*bUsedForStore*/, true/*bStoreAll*/ );
938 void StringResourcePersistenceImpl::implKillRemovedLocaleFiles
940 std::u16string_view Location,
941 const OUString& aNameBase,
942 const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
945 // Delete files for deleted locales
946 for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
948 if( pLocaleItem )
950 OUString aCompleteFileName =
951 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
952 if( xFileAccess->exists( aCompleteFileName ) )
953 xFileAccess->kill( aCompleteFileName );
955 pLocaleItem.reset();
958 m_aDeletedLocaleItemVector.clear();
961 void StringResourcePersistenceImpl::implKillChangedDefaultFiles
963 std::u16string_view Location,
964 const OUString& aNameBase,
965 const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
968 // Delete files for changed defaults
969 for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
971 OUString aCompleteFileName =
972 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location, true );
973 if( xFileAccess->exists( aCompleteFileName ) )
974 xFileAccess->kill( aCompleteFileName );
975 pLocaleItem.reset();
977 m_aChangedDefaultLocaleVector.clear();
980 void StringResourcePersistenceImpl::implStoreAtLocation
982 std::u16string_view Location,
983 const OUString& aNameBase,
984 const OUString& aComment,
985 const Reference< ucb::XSimpleFileAccess3 >& xFileAccess,
986 bool bUsedForStore,
987 bool bStoreAll,
988 bool bKillAll
991 // Delete files for deleted locales
992 if( bUsedForStore || bKillAll )
993 implKillRemovedLocaleFiles( Location, aNameBase, xFileAccess );
995 for( auto& pLocaleItem : m_aLocaleItemVector )
997 if( pLocaleItem != nullptr && (bStoreAll || bKillAll || pLocaleItem->m_bModified) &&
998 loadLocale( pLocaleItem.get() ) )
1000 OUString aCompleteFileName =
1001 implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
1002 if( xFileAccess->exists( aCompleteFileName ) )
1003 xFileAccess->kill( aCompleteFileName );
1005 if( !bKillAll )
1007 // Create Output stream
1008 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1009 if( xOutputStream.is() )
1011 implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
1012 xOutputStream->closeOutput();
1014 if( bUsedForStore )
1015 pLocaleItem->m_bModified = false;
1020 // Delete files for changed defaults
1021 if( bUsedForStore || bKillAll )
1022 implKillChangedDefaultFiles( Location, aNameBase, xFileAccess );
1024 // Default locale
1025 if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || bKillAll || m_bDefaultModified)) )
1026 return;
1028 OUString aCompleteFileName =
1029 implGetPathForLocaleItem( m_pDefaultLocaleItem, aNameBase, Location, true );
1030 if( xFileAccess->exists( aCompleteFileName ) )
1031 xFileAccess->kill( aCompleteFileName );
1033 if( !bKillAll )
1035 // Create Output stream
1036 Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
1037 if( xOutputStream.is() )
1038 xOutputStream->closeOutput();
1040 if( bUsedForStore )
1041 m_bDefaultModified = false;
1046 // BinaryOutput, helper class for exportBinary
1048 class BinaryOutput
1050 rtl::Reference< utl::TempFileFastService > m_xTempFile;
1051 Reference< io::XOutputStream > m_xOutputStream;
1053 public:
1054 explicit BinaryOutput();
1056 const Reference< io::XOutputStream >& getOutputStream() const
1057 { return m_xOutputStream; }
1059 Sequence< ::sal_Int8 > closeAndGetData();
1061 // Template to be used with sal_Int16 and sal_Unicode
1062 template< class T >
1063 void write16BitInt( T n );
1064 void writeInt16( sal_Int16 n )
1065 { write16BitInt( n ); }
1066 void writeUnicodeChar( sal_Unicode n )
1067 { write16BitInt( n ); }
1068 void writeInt32( sal_Int32 n );
1069 void writeString( const OUString& aStr );
1072 BinaryOutput::BinaryOutput()
1074 m_xTempFile = new utl::TempFileFastService;
1075 m_xOutputStream = m_xTempFile;
1078 template< class T >
1079 void BinaryOutput::write16BitInt( T n )
1081 if( !m_xOutputStream.is() )
1082 return;
1084 Sequence< sal_Int8 > aSeq( 2 );
1085 sal_Int8* p = aSeq.getArray();
1087 sal_Int8 nLow = sal_Int8( n & 0xff );
1088 sal_Int8 nHigh = sal_Int8( n >> 8 );
1090 p[0] = nLow;
1091 p[1] = nHigh;
1092 m_xOutputStream->writeBytes( aSeq );
1095 void BinaryOutput::writeInt32( sal_Int32 n )
1097 if( !m_xOutputStream.is() )
1098 return;
1100 Sequence< sal_Int8 > aSeq( 4 );
1101 sal_Int8* p = aSeq.getArray();
1103 for( sal_Int16 i = 0 ; i < 4 ; i++ )
1105 p[i] = sal_Int8( n & 0xff );
1106 n >>= 8;
1108 m_xOutputStream->writeBytes( aSeq );
1111 void BinaryOutput::writeString( const OUString& aStr )
1113 sal_Int32 nLen = aStr.getLength();
1114 const sal_Unicode* pStr = aStr.getStr();
1116 for( sal_Int32 i = 0 ; i < nLen ; i++ )
1117 writeUnicodeChar( pStr[i] );
1119 writeUnicodeChar( 0 );
1122 Sequence< ::sal_Int8 > BinaryOutput::closeAndGetData()
1124 Sequence< ::sal_Int8 > aRetSeq;
1125 if( !m_xOutputStream.is() )
1126 return aRetSeq;
1128 m_xOutputStream->closeOutput();
1130 sal_Int32 nSize = static_cast<sal_Int32>(m_xTempFile->getPosition());
1132 m_xTempFile->seek( 0 );
1133 sal_Int32 nRead = m_xTempFile->readBytes( aRetSeq, nSize );
1134 OSL_ENSURE( nRead == nSize, "BinaryOutput::closeAndGetData: nRead != nSize" );
1136 return aRetSeq;
1140 // Binary format:
1142 // Header
1143 // Byte Content
1144 // 0 + 1 sal_Int16: Version, currently 0, low byte first
1145 // 2 + 3 sal_Int16: Locale count = n, low byte first
1146 // 4 + 5 sal_Int16: Default Locale position in Locale list, == n if none
1147 // 6 - 7 sal_Int32: Start index locale block 0, lowest byte first
1148 // (n-1) * sal_Int32: Start index locale block 1 to n, lowest byte first
1149 // 6 + 4*n sal_Int32: "Start index" non existing locale block n+1,
1150 // marks the first invalid index, kind of EOF
1152 // Locale block
1153 // All strings are stored as 2-Byte-0 terminated sequence
1154 // of 16 bit Unicode characters, each with low byte first
1155 // Empty strings only contain the 2-Byte-0
1157 // Members of com.sun.star.lang.Locale
1158 // with l1 = Locale.Language.getLength()
1159 // with l2 = Locale.Country.getLength()
1160 // with l3 = Locale.Variant.getLength()
1161 // pos0 = 0 Locale.Language
1162 // pos1 = 2 * (l1 + 1) Locale.Country
1163 // pos2 = pos1 + 2 * (l2 + 1) Locale.Variant
1164 // pos3 = pos2 + 2 * (l3 + 1)
1165 // pos3 Properties file written by implWritePropertiesFile
1167 Sequence< sal_Int8 > StringResourcePersistenceImpl::exportBinary( )
1169 BinaryOutput aOut;
1171 sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
1172 std::vector<Sequence< sal_Int8 >> aLocaleDataSeq(nLocaleCount);
1174 sal_Int32 iLocale = 0;
1175 sal_Int32 iDefault = 0;
1176 for( auto& pLocaleItem : m_aLocaleItemVector )
1178 if( pLocaleItem != nullptr && loadLocale( pLocaleItem.get() ) )
1180 if( m_pDefaultLocaleItem == pLocaleItem.get() )
1181 iDefault = iLocale;
1183 BinaryOutput aLocaleOut;
1184 implWriteLocaleBinary( pLocaleItem.get(), aLocaleOut );
1186 aLocaleDataSeq[iLocale] = aLocaleOut.closeAndGetData();
1188 ++iLocale;
1191 // Write header
1192 sal_Int16 nLocaleCount16 = static_cast<sal_Int16>(nLocaleCount);
1193 sal_Int16 iDefault16 = static_cast<sal_Int16>(iDefault);
1194 aOut.writeInt16( 0 ); // nVersion
1195 aOut.writeInt16( nLocaleCount16 );
1196 aOut.writeInt16( iDefault16 );
1198 // Write data positions
1199 sal_Int32 nDataPos = 6 + 4 * (nLocaleCount + 1);
1200 for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1202 aOut.writeInt32( nDataPos );
1204 Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1205 sal_Int32 nSeqLen = rSeq.getLength();
1206 nDataPos += nSeqLen;
1208 // Write final position
1209 aOut.writeInt32( nDataPos );
1211 // Write data
1212 Reference< io::XOutputStream > xOutputStream = aOut.getOutputStream();
1213 if( xOutputStream.is() )
1215 for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
1217 Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
1218 xOutputStream->writeBytes( rSeq );
1222 Sequence< sal_Int8 > aRetSeq = aOut.closeAndGetData();
1223 return aRetSeq;
1226 void StringResourcePersistenceImpl::implWriteLocaleBinary
1227 ( LocaleItem* pLocaleItem, BinaryOutput& rOut )
1229 Reference< io::XOutputStream > xOutputStream = rOut.getOutputStream();
1230 if( !xOutputStream.is() )
1231 return;
1233 Locale& rLocale = pLocaleItem->m_locale;
1234 rOut.writeString( rLocale.Language );
1235 rOut.writeString( rLocale.Country );
1236 rOut.writeString( rLocale.Variant );
1237 implWritePropertiesFile( pLocaleItem, xOutputStream, m_aComment );
1241 // BinaryOutput, helper class for exportBinary
1243 namespace {
1245 class BinaryInput
1247 Sequence< sal_Int8 > m_aData;
1249 const sal_Int8* m_pData;
1250 sal_Int32 m_nCurPos;
1251 sal_Int32 m_nSize;
1253 public:
1254 BinaryInput( const Sequence< ::sal_Int8 >& aData );
1256 Reference< io::XInputStream > getInputStreamForSection( sal_Int32 nSize );
1258 void seek( sal_Int32 nPos );
1259 sal_Int32 getPosition() const
1260 { return m_nCurPos; }
1262 sal_Int16 readInt16();
1263 sal_Int32 readInt32();
1264 sal_Unicode readUnicodeChar();
1265 OUString readString();
1270 BinaryInput::BinaryInput( const Sequence< ::sal_Int8 >& aData )
1271 : m_aData( aData )
1273 m_pData = m_aData.getConstArray();
1274 m_nCurPos = 0;
1275 m_nSize = m_aData.getLength();
1278 Reference< io::XInputStream > BinaryInput::getInputStreamForSection( sal_Int32 nSize )
1280 Reference< io::XInputStream > xIn;
1281 if( m_nCurPos + nSize <= m_nSize )
1283 rtl::Reference< utl::TempFileFastService > xTempOut = new utl::TempFileFastService;
1284 Sequence< sal_Int8 > aSection( m_pData + m_nCurPos, nSize );
1285 xTempOut->writeBytes( aSection );
1286 xTempOut->seek( 0 );
1287 xIn = xTempOut;
1289 else
1290 OSL_FAIL( "BinaryInput::getInputStreamForSection(): Read past end" );
1292 return xIn;
1295 void BinaryInput::seek( sal_Int32 nPos )
1297 if( nPos <= m_nSize )
1298 m_nCurPos = nPos;
1299 else
1300 OSL_FAIL( "BinaryInput::seek(): Position past end" );
1304 sal_Int16 BinaryInput::readInt16()
1306 sal_Int16 nRet = 0;
1307 if( m_nCurPos + 2 <= m_nSize )
1309 nRet = nRet + sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1310 nRet += 256 * sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
1312 else
1313 OSL_FAIL( "BinaryInput::readInt16(): Read past end" );
1315 return nRet;
1318 sal_Int32 BinaryInput::readInt32()
1320 sal_Int32 nRet = 0;
1321 if( m_nCurPos + 4 <= m_nSize )
1323 sal_Int32 nFactor = 1;
1324 for( sal_Int16 i = 0; i < 4; i++ )
1326 nRet += sal_uInt8( m_pData[m_nCurPos++] ) * nFactor;
1327 nFactor *= 256;
1330 else
1331 OSL_FAIL( "BinaryInput::readInt32(): Read past end" );
1333 return nRet;
1336 sal_Unicode BinaryInput::readUnicodeChar()
1338 sal_uInt16 nRet = 0;
1339 if( m_nCurPos + 2 <= m_nSize )
1341 nRet = nRet + sal_uInt8( m_pData[m_nCurPos++] );
1342 nRet += 256 * sal_uInt8( m_pData[m_nCurPos++] );
1344 else
1345 OSL_FAIL( "BinaryInput::readUnicodeChar(): Read past end" );
1347 sal_Unicode cRet = nRet;
1348 return cRet;
1351 OUString BinaryInput::readString()
1353 OUStringBuffer aBuf;
1354 sal_Unicode c;
1357 c = readUnicodeChar();
1358 if( c != 0 )
1359 aBuf.append( c );
1361 while( c != 0 );
1363 OUString aRetStr = aBuf.makeStringAndClear();
1364 return aRetStr;
1367 void StringResourcePersistenceImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
1369 // Init: Remove all locales
1370 sal_Int32 nOldLocaleCount = 0;
1373 Sequence< Locale > aLocaleSeq = getLocales();
1374 nOldLocaleCount = aLocaleSeq.getLength();
1375 if( nOldLocaleCount > 0 )
1377 Locale aLocale = aLocaleSeq[0];
1378 removeLocale( aLocale );
1381 while( nOldLocaleCount > 0 );
1383 // Import data
1384 BinaryInput aIn( Data );
1386 aIn.readInt16(); // version
1387 sal_Int32 nLocaleCount = aIn.readInt16();
1388 sal_Int32 iDefault = aIn.readInt16();
1390 std::unique_ptr<sal_Int32[]> pPositions( new sal_Int32[nLocaleCount + 1] );
1391 for( sal_Int32 i = 0; i < nLocaleCount + 1; i++ )
1392 pPositions[i] = aIn.readInt32();
1394 // Import locales
1395 LocaleItem* pUseAsDefaultItem = nullptr;
1396 for( sal_Int32 i = 0; i < nLocaleCount; i++ )
1398 sal_Int32 nPos = pPositions[i];
1399 aIn.seek( nPos );
1401 Locale aLocale;
1402 aLocale.Language = aIn.readString();
1403 aLocale.Country = aIn.readString();
1404 aLocale.Variant = aIn.readString();
1406 sal_Int32 nAfterStringPos = aIn.getPosition();
1407 sal_Int32 nSize = pPositions[i+1] - nAfterStringPos;
1408 Reference< io::XInputStream > xInput = aIn.getInputStreamForSection( nSize );
1409 if( xInput.is() )
1411 LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale) );
1412 if( iDefault == i )
1413 pUseAsDefaultItem = pLocaleItem;
1414 m_aLocaleItemVector.emplace_back( pLocaleItem );
1415 implReadPropertiesFile( pLocaleItem, xInput );
1419 if( pUseAsDefaultItem != nullptr )
1420 setDefaultLocale( pUseAsDefaultItem->m_locale );
1424 // Private helper methods
1426 static bool checkNamingSceme( std::u16string_view aName, std::u16string_view aNameBase,
1427 Locale& aLocale )
1429 bool bSuccess = false;
1431 size_t nNameLen = aName.size();
1432 size_t nNameBaseLen = aNameBase.size();
1434 // Name has to start with NameBase followed
1435 // by a '_' and at least one more character
1436 if( o3tl::starts_with(aName, aNameBase) && nNameBaseLen < nNameLen-1 &&
1437 aName[nNameBaseLen] == '_' )
1439 bSuccess = true;
1441 /* FIXME-BCP47: this uses '_' underscore character as separator and
1442 * also appends Variant, which can't be blindly changed as it would
1443 * violate the naming scheme in use. */
1445 sal_Int32 iStart = nNameBaseLen + 1;
1446 size_t iNext_ = aName.find( '_', iStart );
1447 if( iNext_ != std::u16string_view::npos && iNext_ < nNameLen-1 )
1449 aLocale.Language = aName.substr( iStart, iNext_ - iStart );
1451 iStart = iNext_ + 1;
1452 iNext_ = aName.find( '_', iStart );
1453 if( iNext_ != std::u16string_view::npos && iNext_ < nNameLen-1 )
1455 aLocale.Country = aName.substr( iStart, iNext_ - iStart );
1456 aLocale.Variant = aName.substr( iNext_ + 1 );
1458 else
1459 aLocale.Country = aName.substr( iStart );
1461 else
1462 aLocale.Language = aName.substr( iStart );
1464 return bSuccess;
1467 void StringResourcePersistenceImpl::implLoadAllLocales()
1469 for( auto& pLocaleItem : m_aLocaleItemVector )
1470 if( pLocaleItem )
1471 loadLocale( pLocaleItem.get() );
1474 // Scan locale properties files helper
1475 void StringResourcePersistenceImpl::implScanLocaleNames( const Sequence< OUString >& aContentSeq )
1477 Locale aDefaultLocale;
1478 bool bDefaultFound = false;
1480 for( const OUString& aCompleteName : aContentSeq )
1482 OUString aPureName;
1483 OUString aExtension;
1484 sal_Int32 iDot = aCompleteName.lastIndexOf( '.' );
1485 sal_Int32 iSlash = aCompleteName.lastIndexOf( '/' );
1486 if( iDot != -1 && iDot > iSlash)
1488 sal_Int32 iCopyFrom = (iSlash != -1) ? iSlash + 1 : 0;
1489 aPureName = aCompleteName.copy( iCopyFrom, iDot-iCopyFrom );
1490 aExtension = aCompleteName.copy( iDot + 1 );
1493 if ( aExtension == "properties" )
1495 //OUString aName = aInetObj.getBase();
1496 Locale aLocale;
1498 if( checkNamingSceme( aPureName, m_aNameBase, aLocale ) )
1500 LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale), false );
1501 m_aLocaleItemVector.emplace_back( pLocaleItem );
1503 if( m_pCurrentLocaleItem == nullptr )
1504 m_pCurrentLocaleItem = pLocaleItem;
1506 if( m_pDefaultLocaleItem == nullptr )
1508 m_pDefaultLocaleItem = pLocaleItem;
1509 m_bDefaultModified = true;
1513 else if( !bDefaultFound && aExtension == "default" )
1515 if( checkNamingSceme( aPureName, m_aNameBase, aDefaultLocale ) )
1516 bDefaultFound = true;
1519 if( bDefaultFound )
1521 LocaleItem* pLocaleItem = getItemForLocale( aDefaultLocale, false );
1522 if( pLocaleItem )
1524 m_pDefaultLocaleItem = pLocaleItem;
1525 m_bDefaultModified = false;
1530 // Scan locale properties files
1531 void StringResourcePersistenceImpl::implScanLocales()
1533 // Dummy implementation, method not called for this
1534 // base class, but pure virtual not possible-
1537 bool StringResourcePersistenceImpl::loadLocale( LocaleItem* pLocaleItem )
1539 bool bSuccess = false;
1541 OSL_ENSURE( pLocaleItem, "StringResourcePersistenceImpl::loadLocale(): pLocaleItem == NULL" );
1542 if( pLocaleItem )
1544 if( pLocaleItem->m_bLoaded )
1546 bSuccess = true;
1548 else
1550 bSuccess = implLoadLocale( pLocaleItem );
1551 pLocaleItem->m_bLoaded = true; // = bSuccess??? -> leads to more tries
1554 return bSuccess;
1557 bool StringResourcePersistenceImpl::implLoadLocale( LocaleItem* )
1559 // Dummy implementation, method not called for this
1560 // base class, but pure virtual not possible-
1561 return false;
1564 static OUString implGetNameScemeForLocaleItem( const LocaleItem* pLocaleItem )
1566 /* FIXME-BCP47: this uses '_' underscore character as separator and
1567 * also appends Variant, which can't be blindly changed as it would
1568 * violate the naming scheme in use. */
1570 static const char aUnder[] = "_";
1572 OSL_ENSURE( pLocaleItem,
1573 "StringResourcePersistenceImpl::implGetNameScemeForLocaleItem(): pLocaleItem == NULL" );
1574 Locale aLocale = pLocaleItem->m_locale;
1576 OUString aRetStr = aUnder + aLocale.Language;
1578 OUString aCountry = aLocale.Country;
1579 if( !aCountry.isEmpty() )
1581 aRetStr += aUnder + aCountry;
1584 OUString aVariant = aLocale.Variant;
1585 if( !aVariant.isEmpty() )
1587 aRetStr += aUnder + aVariant;
1589 return aRetStr;
1592 OUString StringResourcePersistenceImpl::implGetFileNameForLocaleItem
1593 ( LocaleItem const * pLocaleItem, const OUString& aNameBase )
1595 OUString aFileName = aNameBase;
1596 if( aFileName.isEmpty() )
1597 aFileName = aNameBaseDefaultStr;
1599 aFileName += implGetNameScemeForLocaleItem( pLocaleItem );
1600 return aFileName;
1603 OUString StringResourcePersistenceImpl::implGetPathForLocaleItem
1604 ( LocaleItem const * pLocaleItem, const OUString& aNameBase,
1605 std::u16string_view aLocation, bool bDefaultFile )
1607 OUString aFileName = implGetFileNameForLocaleItem( pLocaleItem, aNameBase );
1608 INetURLObject aInetObj( aLocation );
1609 aInetObj.insertName( aFileName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All );
1610 if( bDefaultFile )
1611 aInetObj.setExtension( u"default" );
1612 else
1613 aInetObj.setExtension( u"properties" );
1614 OUString aCompleteFileName = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1615 return aCompleteFileName;
1618 // White space according to Java property files specification in
1619 // http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#load(java.io.InputStream)
1620 static bool isWhiteSpace( sal_Unicode c )
1622 bool bWhite = ( c == 0x0020 || // space
1623 c == 0x0009 || // tab
1624 c == 0x000a || // line feed, not always handled by TextInputStream
1625 c == 0x000d || // carriage return, not always handled by TextInputStream
1626 c == 0x000C ); // form feed
1627 return bWhite;
1630 static void skipWhites( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1632 while( ri < nLen )
1634 if( !isWhiteSpace( pBuf[ri] ) )
1635 break;
1636 ri++;
1640 static bool isHexDigit( sal_Unicode c, sal_uInt16& nDigitVal )
1642 bool bRet = true;
1643 if( c >= '0' && c <= '9' )
1644 nDigitVal = c - '0';
1645 else if( c >= 'a' && c <= 'f' )
1646 nDigitVal = c - 'a' + 10;
1647 else if( c >= 'A' && c <= 'F' )
1648 nDigitVal = c - 'A' + 10;
1649 else
1650 bRet = false;
1651 return bRet;
1654 static sal_Unicode getEscapeChar( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
1656 sal_Int32 i = ri;
1658 sal_Unicode cRet = 0;
1659 sal_Unicode c = pBuf[i];
1660 switch( c )
1662 case 't':
1663 cRet = 0x0009;
1664 break;
1665 case 'n':
1666 cRet = 0x000a;
1667 break;
1668 case 'f':
1669 cRet = 0x000c;
1670 break;
1671 case 'r':
1672 cRet = 0x000d;
1673 break;
1674 case '\\':
1675 cRet = '\\';
1676 break;
1677 case 'u':
1679 // Skip multiple u
1680 i++;
1681 while( i < nLen && pBuf[i] == 'u' )
1682 i++;
1684 // Process hex digits
1685 sal_Int32 nDigitCount = 0;
1686 sal_uInt16 nDigitVal;
1687 while( i < nLen && isHexDigit( pBuf[i], nDigitVal ) )
1689 cRet = 16 * cRet + nDigitVal;
1691 nDigitCount++;
1692 if( nDigitCount == 4 )
1694 // Write back position
1695 ri = i;
1696 break;
1698 i++;
1700 break;
1702 default:
1703 cRet = c;
1706 return cRet;
1709 static void CheckContinueInNextLine( const Reference< io::XTextInputStream2 >& xTextInputStream,
1710 OUString& aLine, bool& bEscapePending, const sal_Unicode*& pBuf,
1711 sal_Int32& nLen, sal_Int32& i )
1713 if( !(i == nLen && bEscapePending) )
1714 return;
1716 bEscapePending = false;
1718 if( !xTextInputStream->isEOF() )
1720 aLine = xTextInputStream->readLine();
1721 nLen = aLine.getLength();
1722 pBuf = aLine.getStr();
1723 i = 0;
1725 skipWhites( pBuf, nLen, i );
1729 bool StringResourcePersistenceImpl::implReadPropertiesFile
1730 ( LocaleItem* pLocaleItem, 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( aResourceID );
1843 IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1844 rIndexMap[ aResourceID ] = pLocaleItem->m_nNextIndex++;
1847 return true;
1851 static sal_Unicode getHexCharForDigit( sal_uInt16 nDigitVal )
1853 sal_Unicode cRet = ( nDigitVal < 10 ) ? ('0' + nDigitVal) : ('a' + (nDigitVal-10));
1854 return cRet;
1857 static void implWriteCharToBuffer( OUStringBuffer& aBuf, sal_Unicode cu, bool bKey )
1859 if( cu == '\\' )
1861 aBuf.append( '\\' );
1862 aBuf.append( '\\' );
1864 else if( cu == 0x000a )
1866 aBuf.append( '\\' );
1867 aBuf.append( 'n' );
1869 else if( cu == 0x000d )
1871 aBuf.append( '\\' );
1872 aBuf.append( 'r' );
1874 else if( bKey && cu == '=' )
1876 aBuf.append( '\\' );
1877 aBuf.append( '=' );
1879 else if( bKey && cu == ':' )
1881 aBuf.append( '\\' );
1882 aBuf.append( ':' );
1884 // ISO/IEC 8859-1 range according to:
1885 // http://en.wikipedia.org/wiki/ISO/IEC_8859-1
1886 else if( cu >= 0x20 && cu <= 0x7e )
1887 //TODO: Check why (cu >= 0xa0 && cu <= 0xFF)
1888 //is encoded in sample properties files
1889 //else if( (cu >= 0x20 && cu <= 0x7e) ||
1890 // (cu >= 0xa0 && cu <= 0xFF) )
1892 aBuf.append( cu );
1894 else
1896 // Unicode encoding
1897 aBuf.append( '\\' );
1898 aBuf.append( 'u' );
1900 sal_uInt16 nVal = cu;
1901 for( sal_uInt16 i = 0 ; i < 4 ; i++ )
1903 sal_uInt16 nDigit = nVal / 0x1000;
1904 nVal -= nDigit * 0x1000;
1905 nVal *= 0x10;
1906 aBuf.append( getHexCharForDigit( nDigit ) );
1911 static void implWriteStringWithEncoding( const OUString& aStr,
1912 Reference< io::XTextOutputStream2 > const & xTextOutputStream, bool bKey )
1914 static const sal_Unicode cLineFeed = 0xa;
1916 OUStringBuffer aBuf;
1917 sal_Int32 nLen = aStr.getLength();
1918 const sal_Unicode* pSrc = aStr.getStr();
1919 for( sal_Int32 i = 0 ; i < nLen ; i++ )
1921 sal_Unicode cu = pSrc[i];
1922 implWriteCharToBuffer( aBuf, cu, bKey );
1923 // TODO?: split long lines
1925 if( !bKey )
1926 aBuf.append( cLineFeed );
1928 OUString aWriteStr = aBuf.makeStringAndClear();
1929 xTextOutputStream->writeString( aWriteStr );
1932 bool StringResourcePersistenceImpl::implWritePropertiesFile( LocaleItem const * pLocaleItem,
1933 const Reference< io::XOutputStream >& xOutputStream, const OUString& aComment )
1935 if( !xOutputStream.is() || pLocaleItem == nullptr )
1936 return false;
1938 bool bSuccess = false;
1939 Reference< io::XTextOutputStream2 > xTextOutputStream = io::TextOutputStream::create(m_xContext);
1941 xTextOutputStream->setOutputStream( xOutputStream );
1943 OUString aEncodingStr = OUString::createFromAscii
1944 ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
1945 xTextOutputStream->setEncoding( aEncodingStr );
1947 xTextOutputStream->writeString( aComment );
1948 xTextOutputStream->writeString( "\n" );
1950 const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
1951 if( !rHashMap.empty() )
1953 // Sort ids according to read order
1954 const IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
1956 // Find max/min index
1957 auto itMinMax = std::minmax_element(rIndexMap.begin(), rIndexMap.end(),
1958 [](const IdToIndexMap::value_type& a, const IdToIndexMap::value_type& b) { return a.second < b.second; });
1959 sal_Int32 nMinIndex = itMinMax.first->second;
1960 sal_Int32 nMaxIndex = itMinMax.second->second;
1961 sal_Int32 nTabSize = nMaxIndex - nMinIndex + 1;
1963 // Create sorted array of pointers to the id strings
1964 std::unique_ptr<const OUString*[]> pIdPtrs( new const OUString*[nTabSize] );
1965 for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
1966 pIdPtrs[i] = nullptr;
1967 for( const auto& rIndex : rIndexMap )
1969 sal_Int32 nIndex = rIndex.second;
1970 pIdPtrs[nIndex - nMinIndex] = &(rIndex.first);
1973 // Write lines in correct order
1974 for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
1976 const OUString* pStr = pIdPtrs[i];
1977 if( pStr != nullptr )
1979 OUString aResourceID = *pStr;
1980 IdToStringMap::const_iterator it = rHashMap.find( aResourceID );
1981 if( it != rHashMap.end() )
1983 implWriteStringWithEncoding( aResourceID, xTextOutputStream, true );
1984 xTextOutputStream->writeString( "=" );
1985 OUString aValStr = (*it).second;
1986 implWriteStringWithEncoding( aValStr, xTextOutputStream, false );
1992 bSuccess = true;
1994 return bSuccess;
1998 // StringResourceWithStorageImpl
2000 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2001 scripting_StringResourceWithStorageImpl_get_implementation(
2002 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2004 return cppu::acquire(new StringResourceWithStorageImpl(context));
2008 StringResourceWithStorageImpl::StringResourceWithStorageImpl( const Reference< XComponentContext >& rxContext )
2009 : StringResourceWithStorageImpl_BASE( rxContext )
2010 , m_bStorageChanged( false )
2015 StringResourceWithStorageImpl::~StringResourceWithStorageImpl()
2020 // XServiceInfo
2023 OUString StringResourceWithStorageImpl::getImplementationName( )
2025 return "com.sun.star.comp.scripting.StringResourceWithStorage";
2028 sal_Bool StringResourceWithStorageImpl::supportsService( const OUString& rServiceName )
2030 return cppu::supportsService(this, rServiceName);
2033 Sequence< OUString > StringResourceWithStorageImpl::getSupportedServiceNames( )
2035 return { "com.sun.star.resource.StringResourceWithStorage" };
2039 // XInitialization
2042 void StringResourceWithStorageImpl::initialize( const Sequence< Any >& aArguments )
2044 std::unique_lock aGuard( m_aMutex );
2046 if ( aArguments.getLength() != 5 )
2048 throw RuntimeException(
2049 "StringResourceWithStorageImpl::initialize: invalid number of arguments!" );
2052 bool bOk = (aArguments[0] >>= m_xStorage);
2053 if( bOk && !m_xStorage.is() )
2054 bOk = false;
2056 if( !bOk )
2058 throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid storage", Reference< XInterface >(), 0 );
2061 implInitializeCommonParameters( aGuard, aArguments );
2065 // Forwarding calls to base class
2067 // XModifyBroadcaster
2068 void StringResourceWithStorageImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2070 StringResourceImpl::addModifyListener( aListener );
2072 void StringResourceWithStorageImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2074 StringResourceImpl::removeModifyListener( aListener );
2077 // XStringResourceResolver
2078 OUString StringResourceWithStorageImpl::resolveString( const OUString& ResourceID )
2080 return StringResourceImpl::resolveString( ResourceID ) ;
2082 OUString StringResourceWithStorageImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2084 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2086 sal_Bool StringResourceWithStorageImpl::hasEntryForId( const OUString& ResourceID )
2088 return StringResourceImpl::hasEntryForId( ResourceID ) ;
2090 sal_Bool StringResourceWithStorageImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2091 const Locale& locale )
2093 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2095 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDs( )
2097 return StringResourceImpl::getResourceIDs();
2099 Sequence< OUString > StringResourceWithStorageImpl::getResourceIDsForLocale
2100 ( const Locale& locale )
2102 return StringResourceImpl::getResourceIDsForLocale( locale );
2104 Locale StringResourceWithStorageImpl::getCurrentLocale()
2106 return StringResourceImpl::getCurrentLocale();
2108 Locale StringResourceWithStorageImpl::getDefaultLocale( )
2110 return StringResourceImpl::getDefaultLocale();
2112 Sequence< Locale > StringResourceWithStorageImpl::getLocales( )
2114 return StringResourceImpl::getLocales();
2117 // XStringResourceManager
2118 sal_Bool StringResourceWithStorageImpl::isReadOnly()
2120 return StringResourceImpl::isReadOnly();
2122 void StringResourceWithStorageImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2124 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2126 void StringResourceWithStorageImpl::setDefaultLocale( const Locale& locale )
2128 StringResourceImpl::setDefaultLocale( locale );
2130 void StringResourceWithStorageImpl::setString( const OUString& ResourceID, const OUString& Str )
2132 StringResourceImpl::setString( ResourceID, Str );
2134 void StringResourceWithStorageImpl::setStringForLocale
2135 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2137 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2139 void StringResourceWithStorageImpl::removeId( const OUString& ResourceID )
2141 StringResourceImpl::removeId( ResourceID );
2143 void StringResourceWithStorageImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2145 StringResourceImpl::removeIdForLocale( ResourceID, locale );
2147 void StringResourceWithStorageImpl::newLocale( const Locale& locale )
2149 StringResourceImpl::newLocale( locale );
2151 void StringResourceWithStorageImpl::removeLocale( const Locale& locale )
2153 StringResourceImpl::removeLocale( locale );
2155 sal_Int32 StringResourceWithStorageImpl::getUniqueNumericId( )
2157 return StringResourceImpl::getUniqueNumericId();
2160 // XStringResourcePersistence
2161 void StringResourceWithStorageImpl::store()
2163 std::unique_lock aGuard( m_aMutex );
2164 implCheckReadOnly( "StringResourceWithStorageImpl::store(): Read only" );
2166 bool bStoreAll = m_bStorageChanged;
2167 m_bStorageChanged = false;
2168 if( !m_bModified && !bStoreAll )
2169 return;
2171 implStoreAtStorage( m_aNameBase, m_aComment, m_xStorage, true/*bUsedForStore*/, bStoreAll );
2172 m_bModified = false;
2175 sal_Bool StringResourceWithStorageImpl::isModified( )
2177 return StringResourcePersistenceImpl::isModified();
2179 void StringResourceWithStorageImpl::setComment( const OUString& Comment )
2181 StringResourcePersistenceImpl::setComment( Comment );
2183 void StringResourceWithStorageImpl::storeToStorage( const Reference< XStorage >& Storage,
2184 const OUString& NameBase, const OUString& Comment )
2186 StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2188 void StringResourceWithStorageImpl::storeToURL( const OUString& URL,
2189 const OUString& NameBase, const OUString& Comment,
2190 const Reference< css::task::XInteractionHandler >& Handler )
2192 StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2194 Sequence< ::sal_Int8 > StringResourceWithStorageImpl::exportBinary( )
2196 return StringResourcePersistenceImpl::exportBinary();
2198 void StringResourceWithStorageImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2200 StringResourcePersistenceImpl::importBinary( Data );
2204 // XStringResourceWithStorage
2206 void StringResourceWithStorageImpl::storeAsStorage( const Reference< XStorage >& Storage )
2208 setStorage( Storage );
2209 store();
2212 void StringResourceWithStorageImpl::setStorage( const Reference< XStorage >& Storage )
2214 std::unique_lock aGuard( m_aMutex );
2216 if( !Storage.is() )
2218 throw IllegalArgumentException( "StringResourceWithStorageImpl::setStorage: invalid storage", Reference< XInterface >(), 0 );
2221 implLoadAllLocales();
2223 m_xStorage = Storage;
2224 m_bStorageChanged = true;
2228 // Private helper methods
2231 // Scan locale properties files
2232 void StringResourceWithStorageImpl::implScanLocales()
2234 if( m_xStorage.is() )
2236 Sequence< OUString > aContentSeq = m_xStorage->getElementNames();
2237 implScanLocaleNames( aContentSeq );
2240 implLoadAllLocales();
2243 // Loading
2244 bool StringResourceWithStorageImpl::implLoadLocale( LocaleItem* pLocaleItem )
2246 bool bSuccess = false;
2249 OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem, m_aNameBase ) + ".properties";
2251 Reference< io::XStream > xElementStream =
2252 m_xStorage->openStreamElement( aStreamName, ElementModes::READ );
2254 if( xElementStream.is() )
2256 Reference< io::XInputStream > xInputStream = xElementStream->getInputStream();
2257 if( xInputStream.is() )
2259 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
2260 xInputStream->closeInput();
2264 catch( uno::Exception& )
2267 return bSuccess;
2271 // StringResourceWithLocationImpl
2274 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2275 scripting_StringResourceWithLocationImpl_get_implementation(
2276 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
2278 return cppu::acquire(new StringResourceWithLocationImpl(context));
2283 StringResourceWithLocationImpl::StringResourceWithLocationImpl( const Reference< XComponentContext >& rxContext )
2284 : StringResourceWithLocationImpl_BASE( rxContext )
2285 , m_bLocationChanged( false )
2290 StringResourceWithLocationImpl::~StringResourceWithLocationImpl()
2295 // XServiceInfo
2298 OUString StringResourceWithLocationImpl::getImplementationName( )
2300 return "com.sun.star.comp.scripting.StringResourceWithLocation";
2303 sal_Bool StringResourceWithLocationImpl::supportsService( const OUString& rServiceName )
2305 return cppu::supportsService(this, rServiceName);
2308 Sequence< OUString > StringResourceWithLocationImpl::getSupportedServiceNames( )
2310 return { "com.sun.star.resource.StringResourceWithLocation" };
2314 // XInitialization
2317 void StringResourceWithLocationImpl::initialize( const Sequence< Any >& aArguments )
2319 std::unique_lock aGuard( m_aMutex );
2321 if ( aArguments.getLength() != 6 )
2323 throw RuntimeException(
2324 "XInitialization::initialize: invalid number of arguments!" );
2327 bool bOk = (aArguments[0] >>= m_aLocation);
2328 sal_Int32 nLen = m_aLocation.getLength();
2329 if( bOk && nLen == 0 )
2331 bOk = false;
2333 else
2335 if( m_aLocation[nLen - 1] != '/' )
2336 m_aLocation += "/";
2339 if( !bOk )
2341 throw IllegalArgumentException( "XInitialization::initialize: invalid URL", Reference< XInterface >(), 0 );
2345 bOk = (aArguments[5] >>= m_xInteractionHandler);
2346 if( !bOk )
2348 throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid type", Reference< XInterface >(), 5 );
2351 implInitializeCommonParameters( aGuard, aArguments );
2355 // Forwarding calls to base class
2357 // XModifyBroadcaster
2358 void StringResourceWithLocationImpl::addModifyListener( const Reference< XModifyListener >& aListener )
2360 StringResourceImpl::addModifyListener( aListener );
2362 void StringResourceWithLocationImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
2364 StringResourceImpl::removeModifyListener( aListener );
2367 // XStringResourceResolver
2368 OUString StringResourceWithLocationImpl::resolveString( const OUString& ResourceID )
2370 return StringResourceImpl::resolveString( ResourceID ) ;
2372 OUString StringResourceWithLocationImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
2374 return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
2376 sal_Bool StringResourceWithLocationImpl::hasEntryForId( const OUString& ResourceID )
2378 return StringResourceImpl::hasEntryForId( ResourceID ) ;
2380 sal_Bool StringResourceWithLocationImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
2381 const Locale& locale )
2383 return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
2385 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDs( )
2387 return StringResourceImpl::getResourceIDs();
2389 Sequence< OUString > StringResourceWithLocationImpl::getResourceIDsForLocale
2390 ( const Locale& locale )
2392 return StringResourceImpl::getResourceIDsForLocale( locale );
2394 Locale StringResourceWithLocationImpl::getCurrentLocale()
2396 return StringResourceImpl::getCurrentLocale();
2398 Locale StringResourceWithLocationImpl::getDefaultLocale( )
2400 return StringResourceImpl::getDefaultLocale();
2402 Sequence< Locale > StringResourceWithLocationImpl::getLocales( )
2404 return StringResourceImpl::getLocales();
2407 // XStringResourceManager
2408 sal_Bool StringResourceWithLocationImpl::isReadOnly()
2410 return StringResourceImpl::isReadOnly();
2412 void StringResourceWithLocationImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
2414 StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
2416 void StringResourceWithLocationImpl::setDefaultLocale( const Locale& locale )
2418 StringResourceImpl::setDefaultLocale( locale );
2420 void StringResourceWithLocationImpl::setString( const OUString& ResourceID, const OUString& Str )
2422 StringResourceImpl::setString( ResourceID, Str );
2424 void StringResourceWithLocationImpl::setStringForLocale
2425 ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
2427 StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
2429 void StringResourceWithLocationImpl::removeId( const OUString& ResourceID )
2431 StringResourceImpl::removeId( ResourceID );
2433 void StringResourceWithLocationImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
2435 StringResourceImpl::removeIdForLocale( ResourceID, locale );
2437 void StringResourceWithLocationImpl::newLocale( const Locale& locale )
2439 StringResourceImpl::newLocale( locale );
2441 void StringResourceWithLocationImpl::removeLocale( const Locale& locale )
2443 StringResourceImpl::removeLocale( locale );
2445 sal_Int32 StringResourceWithLocationImpl::getUniqueNumericId( )
2447 return StringResourceImpl::getUniqueNumericId();
2450 // XStringResourcePersistence
2451 void StringResourceWithLocationImpl::store()
2453 std::unique_lock aGuard( m_aMutex );
2454 implCheckReadOnly( "StringResourceWithLocationImpl::store(): Read only" );
2456 bool bStoreAll = m_bLocationChanged;
2457 m_bLocationChanged = false;
2458 if( !m_bModified && !bStoreAll )
2459 return;
2461 Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2462 implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
2463 xFileAccess, true/*bUsedForStore*/, bStoreAll );
2464 m_bModified = false;
2467 sal_Bool StringResourceWithLocationImpl::isModified( )
2469 return StringResourcePersistenceImpl::isModified();
2471 void StringResourceWithLocationImpl::setComment( const OUString& Comment )
2473 StringResourcePersistenceImpl::setComment( Comment );
2475 void StringResourceWithLocationImpl::storeToStorage( const Reference< XStorage >& Storage,
2476 const OUString& NameBase, const OUString& Comment )
2478 StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
2480 void StringResourceWithLocationImpl::storeToURL( const OUString& URL,
2481 const OUString& NameBase, const OUString& Comment,
2482 const Reference< css::task::XInteractionHandler >& Handler )
2484 StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
2486 Sequence< ::sal_Int8 > StringResourceWithLocationImpl::exportBinary( )
2488 return StringResourcePersistenceImpl::exportBinary();
2490 void StringResourceWithLocationImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
2492 StringResourcePersistenceImpl::importBinary( Data );
2496 // XStringResourceWithLocation
2498 // XStringResourceWithLocation
2499 void StringResourceWithLocationImpl::storeAsURL( const OUString& URL )
2501 setURL( URL );
2502 store();
2505 void StringResourceWithLocationImpl::setURL( const OUString& URL )
2507 std::unique_lock aGuard( m_aMutex );
2508 implCheckReadOnly( "StringResourceWithLocationImpl::setURL(): Read only" );
2510 sal_Int32 nLen = URL.getLength();
2511 if( nLen == 0 )
2513 throw IllegalArgumentException( "StringResourceWithLocationImpl::setURL: invalid URL", Reference< XInterface >(), 0 );
2516 implLoadAllLocales();
2518 // Delete files at old location
2519 implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
2520 getFileAccessImpl(), false/*bUsedForStore*/, false/*bStoreAll*/, true/*bKillAll*/ );
2522 m_aLocation = URL;
2523 m_bLocationChanged = true;
2527 // Private helper methods
2530 // Scan locale properties files
2531 void StringResourceWithLocationImpl::implScanLocales()
2533 const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2534 if( xFileAccess.is() && xFileAccess->isFolder( m_aLocation ) )
2536 Sequence< OUString > aContentSeq = xFileAccess->getFolderContents( m_aLocation, false );
2537 implScanLocaleNames( aContentSeq );
2541 // Loading
2542 bool StringResourceWithLocationImpl::implLoadLocale( LocaleItem* pLocaleItem )
2544 bool bSuccess = false;
2546 const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
2547 if( xFileAccess.is() )
2549 OUString aCompleteFileName =
2550 implGetPathForLocaleItem( pLocaleItem, m_aNameBase, m_aLocation );
2552 Reference< io::XInputStream > xInputStream;
2555 xInputStream = xFileAccess->openFileRead( aCompleteFileName );
2557 catch( Exception& )
2559 if( xInputStream.is() )
2561 bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
2562 xInputStream->closeInput();
2566 return bSuccess;
2569 const Reference< ucb::XSimpleFileAccess3 > & StringResourceWithLocationImpl::getFileAccessImpl()
2571 if( !m_xSFI.is() )
2573 m_xSFI = ucb::SimpleFileAccess::create(m_xContext);
2575 if( m_xSFI.is() && m_xInteractionHandler.is() )
2576 m_xSFI->setInteractionHandler( m_xInteractionHandler );
2578 return m_xSFI;
2581 } // namespace stringresource
2584 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */